first commit
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
17
eslint.config.js
Normal file
@ -0,0 +1,17 @@
|
||||
import js from '@eslint/js'
|
||||
import pluginVue from 'eslint-plugin-vue'
|
||||
|
||||
export default [
|
||||
{
|
||||
name: 'app/files-to-lint',
|
||||
files: ['**/*.{js,mjs,jsx,vue}'],
|
||||
},
|
||||
|
||||
{
|
||||
name: 'app/files-to-ignore',
|
||||
ignores: ['**/dist/**', '**/dist-ssr/**', '**/coverage/**'],
|
||||
},
|
||||
|
||||
js.configs.recommended,
|
||||
...pluginVue.configs['flat/essential'],
|
||||
]
|
||||
19
index.html
Normal file
@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<title>Solnus | Legal Service Solutions</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
8
jsconfig.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
4071
package-lock.json
generated
Normal file
24
package.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "vue-project",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint . --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.5.13",
|
||||
"vue-router": "^4.6.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.14.0",
|
||||
"@vitejs/plugin-vue": "^5.2.1",
|
||||
"eslint": "^9.14.0",
|
||||
"eslint-plugin-vue": "^9.30.0",
|
||||
"vite": "^6.0.5",
|
||||
"vite-plugin-vue-devtools": "^7.6.8"
|
||||
}
|
||||
}
|
||||
BIN
public/favicon.ico
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
22
src/App.vue
Normal file
@ -0,0 +1,22 @@
|
||||
<script setup>
|
||||
import BackToTop from "./components/button/BackToTop.vue";
|
||||
import Footer from "./components/footer/Footer.vue";
|
||||
import Navigation from "./components/Navigation.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header>
|
||||
<Navigation />
|
||||
</header>
|
||||
|
||||
<router-view />
|
||||
|
||||
<footer id="kontak">
|
||||
<Footer />
|
||||
</footer>
|
||||
|
||||
<BackToTop />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
BIN
src/assets/background-hero.png
Normal file
|
After Width: | Height: | Size: 234 KiB |
BIN
src/assets/background_hero.png
Normal file
|
After Width: | Height: | Size: 3.2 MiB |
5
src/assets/base.css
Normal file
@ -0,0 +1,5 @@
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-family: "Poppins", serif;
|
||||
}
|
||||
BIN
src/assets/business-hero.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
src/assets/hero.png
Normal file
|
After Width: | Height: | Size: 618 KiB |
BIN
src/assets/icon-check.png
Normal file
|
After Width: | Height: | Size: 190 KiB |
BIN
src/assets/icon-pt.png
Normal file
|
After Width: | Height: | Size: 258 KiB |
BIN
src/assets/icon-question.png
Normal file
|
After Width: | Height: | Size: 288 KiB |
BIN
src/assets/icon_check.png
Normal file
|
After Width: | Height: | Size: 929 B |
BIN
src/assets/icon_law.png
Normal file
|
After Width: | Height: | Size: 686 B |
BIN
src/assets/logo-solnus.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
1
src/assets/logo.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>
|
||||
|
After Width: | Height: | Size: 276 B |
4
src/assets/main.css
Normal file
@ -0,0 +1,4 @@
|
||||
@import './base.css';
|
||||
|
||||
|
||||
|
||||
BIN
src/assets/salad.jpg
Normal file
|
After Width: | Height: | Size: 136 KiB |
BIN
src/assets/service_legal.jpg
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
src/assets/spaghetti.jpg
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
src/assets/steak.jpg
Normal file
|
After Width: | Height: | Size: 71 KiB |
315
src/components/Navigation.vue
Normal file
@ -0,0 +1,315 @@
|
||||
<template>
|
||||
<header>
|
||||
<nav class="navbar">
|
||||
<div class="left">
|
||||
<router-link to="/">
|
||||
<img src="@/assets/logo-solnus.png" class="logo" />
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
<ul class="nav-links" :class="{ active: isMenuOpen }">
|
||||
<li><a href="#solusi" @click.prevent="scrollToSection('solusi')" :class="{ 'active-link': activeSection === 'solusi' }">Solusi</a></li>
|
||||
<li class="dropdown" ref="dropdownRef">
|
||||
<button type="button" class="dropdown-toggle" @click="toggleDropdown" :class="{ 'active-link': $route.path.startsWith('/packages') }">
|
||||
Paket Layanan
|
||||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg" style="margin-left: 4px; transition: transform 0.2s;" :style="{ transform: showDropdown ? 'rotate(180deg)' : 'rotate(0deg)' }">
|
||||
<path d="M3 4.5L6 7.5L9 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="dropdown-menu" :class="{ show: showDropdown }">
|
||||
<router-link to="/packages" @click="closeMenu">Semua Paket</router-link>
|
||||
<router-link to="/packages/pendirian" @click="closeMenu">Paket Pendirian</router-link>
|
||||
<router-link to="/packages/perubahan" @click="closeMenu">Paket Perubahan & Pembubaran</router-link>
|
||||
<router-link to="/packages/property" @click="closeMenu">Paket Property</router-link>
|
||||
<router-link to="/packages/khusus" @click="closeMenu">Paket Khusus</router-link>
|
||||
<router-link to="/packages/umum" @click="closeMenu">Paket Umum</router-link>
|
||||
</div>
|
||||
</li>
|
||||
<li><a href="#kontak" @click.prevent="scrollToSection('kontak')" :class="{ 'active-link': activeSection === 'kontak' }">Kontak Kami</a></li>
|
||||
<li>
|
||||
<a href="#konsultasi" @click.prevent="scrollToSection('konsultasi')" class="consult-btn" :class="{ 'active-link': activeSection === 'konsultasi' }">Konsultasi Gratis</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="hamburger" @click="toggleMenu">
|
||||
<div class="bar"></div>
|
||||
<div class="bar"></div>
|
||||
<div class="bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
isMenuOpen: false,
|
||||
activeSection: '',
|
||||
showDropdown: false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
toggleMenu() {
|
||||
this.isMenuOpen = !this.isMenuOpen;
|
||||
},
|
||||
closeMenu() {
|
||||
this.isMenuOpen = false;
|
||||
this.showDropdown = false;
|
||||
},
|
||||
toggleDropdown() {
|
||||
this.showDropdown = !this.showDropdown;
|
||||
},
|
||||
scrollToSection(sectionId) {
|
||||
this.closeMenu();
|
||||
|
||||
// If not on home page, navigate to home first
|
||||
if (this.$route.path !== '/') {
|
||||
this.$router.push('/').then(() => {
|
||||
// Wait for DOM to be fully rendered
|
||||
setTimeout(() => {
|
||||
this.performScroll(sectionId);
|
||||
}, 100);
|
||||
});
|
||||
} else {
|
||||
this.performScroll(sectionId);
|
||||
}
|
||||
},
|
||||
performScroll(sectionId) {
|
||||
const element = document.getElementById(sectionId);
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
},
|
||||
handleScroll() {
|
||||
// Only track sections on home page
|
||||
if (this.$route.path !== '/') {
|
||||
this.activeSection = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const sections = ['solusi', 'konsultasi', 'kontak'];
|
||||
const scrollPosition = window.scrollY + 100;
|
||||
|
||||
for (const sectionId of sections) {
|
||||
const element = document.getElementById(sectionId);
|
||||
if (element) {
|
||||
const offsetTop = element.offsetTop;
|
||||
const offsetBottom = offsetTop + element.offsetHeight;
|
||||
|
||||
if (scrollPosition >= offsetTop && scrollPosition < offsetBottom) {
|
||||
this.activeSection = sectionId;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.activeSection = '';
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// Close menu when clicking on any nav link
|
||||
const navLinks = this.$el.querySelectorAll('.nav-links a');
|
||||
navLinks.forEach(link => {
|
||||
link.addEventListener('click', () => {
|
||||
this.closeMenu();
|
||||
});
|
||||
});
|
||||
|
||||
// Track scroll position for active section
|
||||
window.addEventListener('scroll', this.handleScroll);
|
||||
this.handleScroll();
|
||||
},
|
||||
beforeUnmount() {
|
||||
window.removeEventListener('scroll', this.handleScroll);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
background: white;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
z-index: 1000;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 140px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 48px;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
list-style: none;
|
||||
display: flex;
|
||||
gap: 50px;
|
||||
align-items: center;
|
||||
margin-right: 20px;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.nav-links li a {
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.nav-links li a:hover {
|
||||
color: #FF6640;
|
||||
}
|
||||
|
||||
.nav-links li a.router-link-active,
|
||||
.nav-links li a.router-link-exact-active,
|
||||
.nav-links li a.active-link {
|
||||
color: #FF6640;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dropdown-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
border: none;
|
||||
color: #333;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
left: 0;
|
||||
background: white;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
padding: 8px 0;
|
||||
margin: 0;
|
||||
min-width: 260px;
|
||||
z-index: 99999;
|
||||
}
|
||||
|
||||
.dropdown-menu.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dropdown-menu a {
|
||||
display: block;
|
||||
padding: 12px 20px;
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
transition: background 0.2s, color 0.2s;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.dropdown-menu a:hover {
|
||||
background: #f9fafb;
|
||||
color: #FF6640;
|
||||
}
|
||||
|
||||
.dropdown-menu a.router-link-active,
|
||||
.dropdown-menu a.router-link-exact-active {
|
||||
color: #FF6640;
|
||||
background: #fff5f3;
|
||||
}
|
||||
|
||||
.consult-btn {
|
||||
padding: 10px 22px;
|
||||
border: 2px solid #FF6640;
|
||||
border-radius: 8px;
|
||||
font-weight: bold;
|
||||
color: #FF6640;
|
||||
white-space: nowrap; /* prevent wrapping */
|
||||
}
|
||||
|
||||
.consult-btn:hover {
|
||||
background: #FF6640;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.hamburger {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
cursor: pointer;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.bar {
|
||||
width: 28px;
|
||||
height: 3px;
|
||||
background: #333;
|
||||
}
|
||||
|
||||
/* Mobile */
|
||||
@media (max-width: 768px) {
|
||||
.nav-links {
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
position: absolute;
|
||||
top: 70px;
|
||||
right: 0;
|
||||
background: white;
|
||||
width: 100%;
|
||||
padding: 20px 0;
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.nav-links.active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.hamburger {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dropdown-toggle {
|
||||
padding: 12px 20px;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
position: static;
|
||||
transform: none;
|
||||
box-shadow: none;
|
||||
border-left: 3px solid #FF6640;
|
||||
background: #f9fafb;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.nav-links > li > a {
|
||||
display: block;
|
||||
padding: 12px 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
46
src/components/button/BackToTop.vue
Normal file
@ -0,0 +1,46 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from "vue";
|
||||
|
||||
const showButton = ref(false);
|
||||
|
||||
const scrollToTop = () => {
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
};
|
||||
|
||||
const handleScroll = () => {
|
||||
showButton.value = window.scrollY > 0;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener("scroll", handleScroll);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener("scroll", handleScroll);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button v-if="showButton" @click="scrollToTop">↑</button>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
button {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background-color: #f8b400;;
|
||||
color: black;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #c39004;;
|
||||
}
|
||||
</style>
|
||||
83
src/components/footer/Footer.vue
Normal file
@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<footer class="footer">
|
||||
<div class="footer-wrapper">
|
||||
<div class="container">
|
||||
<div class="grid">
|
||||
<div class="col brand">
|
||||
<div class="logo">
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="28" height="28" rx="6" fill="#FF6640"/>
|
||||
<path d="M8 10h12M8 14h12M8 18h8" stroke="#fff" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
<span class="logo-text">Solnus</span>
|
||||
</div>
|
||||
<p class="tagline">Urus Legalitas Bisnis Tanpa Ribet.<br/>Dari pendirian PT, CV, hingga Yayasan,<br/>semua proses hukum kini lebih cepat,<br/>aman, dan transparan bersama tim<br/>profesional berpengalaman.</p>
|
||||
<div class="contact">+62 821 xxxx xxxx</div>
|
||||
<div class="social">
|
||||
<a href="#" aria-label="LinkedIn">
|
||||
<svg width="20" height="20" fill="currentColor" viewBox="0 0 24 24"><path d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.268c-.966 0-1.75-.79-1.75-1.764s.784-1.764 1.75-1.764 1.75.79 1.75 1.764-.783 1.764-1.75 1.764zm13.5 12.268h-3v-5.604c0-3.368-4-3.113-4 0v5.604h-3v-11h3v1.765c1.396-2.586 7-2.777 7 2.476v6.759z"/></svg>
|
||||
</a>
|
||||
<a href="#" aria-label="Instagram">
|
||||
<svg width="20" height="20" fill="currentColor" viewBox="0 0 24 24"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<h3 class="title">Navigasi</h3>
|
||||
<ul class="links">
|
||||
<li><a href="#">Solusi</a></li>
|
||||
<li><a href="#">Kenapa Solnus?</a></li>
|
||||
<li><a href="#">Layanan Solnus</a></li>
|
||||
<li><a href="#">Cara Kerja</a></li>
|
||||
<li><a href="#">Testimoni</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<h3 class="title">Layanan</h3>
|
||||
<ul class="links">
|
||||
<li><a href="#">Pendirian PT (Perseroan Terbatas)</a></li>
|
||||
<li><a href="#">Pendirian CV (Commanditaire Vennootschap)</a></li>
|
||||
<li><a href="#">Pendirian Yayasan</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<div class="container">
|
||||
<div class="bottom-content">
|
||||
<span>© 2025 All Rights Reserved | PT Solusi Legal Nusantara</span>
|
||||
<a href="#" class="privacy">Privacy Policy</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.footer { background: #fff; border-top: 1px solid #e5e7eb; }
|
||||
.footer-wrapper { padding: 60px 20px 0; }
|
||||
.container { max-width: 1200px; margin: 0 auto; }
|
||||
.grid { display: grid; gap: 48px; grid-template-columns: 1fr; }
|
||||
@media (min-width: 768px) { .grid { grid-template-columns: 1.5fr 1fr 1fr; } }
|
||||
.col { }
|
||||
.brand { }
|
||||
.logo { display: flex; align-items: center; gap: 10px; margin-bottom: 16px; }
|
||||
.logo-text { font-size: 24px; font-weight: 800; color: #FF6640; }
|
||||
.tagline { font-size: 14px; line-height: 1.7; color: #6b7280; margin: 0 0 20px; }
|
||||
.contact { font-size: 16px; font-weight: 600; color: #111827; margin-bottom: 16px; }
|
||||
.social { display: flex; gap: 12px; }
|
||||
.social a { width: 36px; height: 36px; display: flex; align-items: center; justify-content: center; color: #6b7280; transition: color .2s; }
|
||||
.social a:hover { color: #111827; }
|
||||
.title { font-size: 16px; font-weight: 700; color: #111827; margin: 0 0 16px; }
|
||||
.links { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
|
||||
.links a { color: #6b7280; text-decoration: none; font-size: 14px; transition: color .2s; }
|
||||
.links a:hover { color: #111827; }
|
||||
.bottom { background: #111827; margin-top: 48px; padding: 20px; }
|
||||
.bottom-content { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 12px; color: #9ca3af; font-size: 14px; }
|
||||
.privacy { color: #9ca3af; text-decoration: none; }
|
||||
.privacy:hover { color: #fff; }
|
||||
@media (max-width: 640px) { .bottom-content { flex-direction: column; text-align: center; } }
|
||||
</style>
|
||||
39
src/components/main/CallToAction.vue
Normal file
@ -0,0 +1,39 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
title: { type: String, default: 'Jangan biarkan bisnis hanya Anda berdiri di atas dokumen yang lemah.' },
|
||||
buttonText: { type: String, default: 'Konsultasi Gratis' }
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="cta">
|
||||
<div class="overlay"></div>
|
||||
<div class="container">
|
||||
<h2 class="title">{{ title }}</h2>
|
||||
<button class="btn">{{ buttonText }}</button>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.cta {
|
||||
padding: 80px 20px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
background-image: url('@/assets/business-hero.png');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.65);
|
||||
z-index: 1;
|
||||
}
|
||||
.container { max-width: 800px; margin: 0 auto; position: relative; z-index: 2; }
|
||||
.title { color: #fff; font-size: 28px; font-weight: 700; line-height: 1.4; margin: 0 0 24px; }
|
||||
.btn { padding: 14px 32px; background: #FF6640; color: #fff; border: none; border-radius: 6px; font-weight: 600; font-size: 16px; cursor: pointer; transition: background .2s; }
|
||||
.btn:hover { background: #e85a30; }
|
||||
@media (min-width: 768px) { .title { font-size: 36px; } }
|
||||
</style>
|
||||
174
src/components/main/ContactUs.vue
Normal file
@ -0,0 +1,174 @@
|
||||
<template>
|
||||
<section class="contact">
|
||||
<h2 class="contact-title">{{ heading }}</h2>
|
||||
<p class="contact-description">
|
||||
{{ subheading }}
|
||||
</p>
|
||||
|
||||
<div class="contact-items">
|
||||
<article
|
||||
v-for="item in contactItems"
|
||||
:key="item.id"
|
||||
class="item"
|
||||
>
|
||||
<i aria-hidden="true">{{ item.icon }}</i>
|
||||
<div class="details">
|
||||
<h3>{{ item.title }}</h3>
|
||||
<p class="detail-text">{{ item.detail }}</p>
|
||||
<p
|
||||
v-if="item.description"
|
||||
class="detail-subtext"
|
||||
>
|
||||
{{ item.description }}
|
||||
</p>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const heading = "Hubungi Solnus";
|
||||
const subheading =
|
||||
"Tim kami siap mendampingi legalitas bisnis Anda melalui jalur komunikasi yang paling nyaman.";
|
||||
|
||||
const contactItems = [
|
||||
{
|
||||
id: "phone",
|
||||
icon: "📞",
|
||||
title: "Telepon",
|
||||
detail: "+62 811-2233-4455",
|
||||
description: "Senin - Jumat, 09.00 - 18.00 WIB"
|
||||
},
|
||||
{
|
||||
id: "email",
|
||||
icon: "✉️",
|
||||
title: "Email",
|
||||
detail: "support@solnus.id",
|
||||
description: "Kami membalas dalam 1x24 jam kerja"
|
||||
},
|
||||
{
|
||||
id: "office",
|
||||
icon: "📍",
|
||||
title: "Kantor",
|
||||
detail: "Green Office Park, BSD City",
|
||||
description: "Silakan buat janji sebelum kunjungan"
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.contact {
|
||||
padding: 80px 20px;
|
||||
}
|
||||
|
||||
.contact-title {
|
||||
text-align: center;
|
||||
font-size: 2.2rem;
|
||||
margin-bottom: 10px;
|
||||
color: var(--color-heading);
|
||||
}
|
||||
|
||||
.contact-description {
|
||||
text-align: center;
|
||||
max-width: 640px;
|
||||
margin: 0 auto 40px;
|
||||
color: var(--color-text);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.contact-items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.item {
|
||||
margin-top: 2rem;
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.details {
|
||||
flex: 1;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
i {
|
||||
display: flex;
|
||||
place-items: center;
|
||||
place-content: center;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
|
||||
color: var(--color-text);
|
||||
background: var(--color-background-mute);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.4rem;
|
||||
color: var(--color-heading);
|
||||
}
|
||||
|
||||
.detail-text {
|
||||
font-weight: 600;
|
||||
color: var(--color-heading);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.detail-subtext {
|
||||
margin: 0.2rem 0 0;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.contact-items {
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
.item {
|
||||
margin-top: 0;
|
||||
padding: 0.4rem 0 1rem calc(var(--section-gap) / 2);
|
||||
}
|
||||
|
||||
i {
|
||||
top: calc(50% - 25px);
|
||||
left: -26px;
|
||||
position: absolute;
|
||||
border: 1px solid var(--color-border);
|
||||
background: var(--color-background);
|
||||
border-radius: 8px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.item:before {
|
||||
content: ' ';
|
||||
border-left: 1px solid var(--color-border);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: calc(50% + 25px);
|
||||
height: calc(50% - 25px);
|
||||
}
|
||||
|
||||
.item:after {
|
||||
content: ' ';
|
||||
border-left: 1px solid var(--color-border);
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: calc(50% + 25px);
|
||||
height: calc(50% - 25px);
|
||||
}
|
||||
|
||||
.item:first-of-type:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.item:last-of-type:after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
209
src/components/main/Hero.vue
Normal file
@ -0,0 +1,209 @@
|
||||
<template>
|
||||
<section
|
||||
class="hero"
|
||||
:style="heroStyle"
|
||||
>
|
||||
<div class="overlay">
|
||||
<div class="hero-content">
|
||||
<h1 class="headline">{{ headline }}</h1>
|
||||
|
||||
<div class="badges">
|
||||
<button
|
||||
v-for="badge in badges"
|
||||
:key="badge.id"
|
||||
class="badge"
|
||||
:class="badge.variant"
|
||||
type="button"
|
||||
>
|
||||
<img
|
||||
:src="badge.icon"
|
||||
:alt="badge.alt"
|
||||
class="icon"
|
||||
/>
|
||||
{{ badge.label }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<p class="description">
|
||||
{{ description }}
|
||||
</p>
|
||||
|
||||
<div class="cta-buttons">
|
||||
<button
|
||||
v-for="button in ctaButtons"
|
||||
:key="button.id"
|
||||
class="cta-button"
|
||||
:class="button.variant"
|
||||
type="button"
|
||||
>
|
||||
{{ button.label }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import heroBackground from "@/assets/background_hero.png";
|
||||
import iconLaw from "@/assets/icon_law.png";
|
||||
import iconCheck from "@/assets/icon_check.png";
|
||||
|
||||
const headline = "Urus Legalitas Bisnis Tanpa Ribet";
|
||||
const description =
|
||||
"Dari pendirian PT, CV, hingga Yayasan, semua proses hukum lebih cepat, aman, dan transparan bersama tim profesional berpengalaman Solnus secara online.";
|
||||
|
||||
const heroStyle = {
|
||||
backgroundImage: `url(${heroBackground})`
|
||||
};
|
||||
|
||||
const badges = [
|
||||
{
|
||||
id: "law-support",
|
||||
label: "Didampingi Tim Hukum",
|
||||
variant: "primary",
|
||||
icon: iconLaw,
|
||||
alt: "Ikon tim hukum"
|
||||
},
|
||||
{
|
||||
id: "verified-partner",
|
||||
label: "Legal Partner Terverifikasi",
|
||||
variant: "secondary",
|
||||
icon: iconCheck,
|
||||
alt: "Ikon verifikasi legal"
|
||||
}
|
||||
];
|
||||
|
||||
const ctaButtons = [
|
||||
{ id: "consult", label: "Jadwalkan Konsultasi Gratis", variant: "primary" },
|
||||
{ id: "services", label: "Lihat Layanan Solnus", variant: "secondary" }
|
||||
];
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.hero {
|
||||
position: relative;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 40px;
|
||||
color: white;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
max-width: 900px;
|
||||
margin-top: -40px;
|
||||
}
|
||||
|
||||
.headline {
|
||||
font-size: 4rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.2;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.badges {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
justify-content: center;
|
||||
margin-bottom: 30px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px 22px;
|
||||
font-weight: 600;
|
||||
border-radius: 8px;
|
||||
border: none;
|
||||
cursor: default;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.badge.primary {
|
||||
background-color: #b71c1c;
|
||||
}
|
||||
|
||||
.badge.secondary {
|
||||
background-color: #5e0000;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.cta-buttons {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.cta-button {
|
||||
padding: 14px 28px;
|
||||
font-size: 1.1rem;
|
||||
border-radius: 10px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.cta-button.primary {
|
||||
background-color: #FF6640;
|
||||
color: black;
|
||||
border-color: #FF6640;
|
||||
}
|
||||
|
||||
.cta-button.primary:hover {
|
||||
background-color: #FF6640;
|
||||
border-color: #FF6640;
|
||||
}
|
||||
|
||||
.cta-button.secondary {
|
||||
background-color: transparent;
|
||||
color: white;
|
||||
border-color: white;
|
||||
}
|
||||
|
||||
.cta-button.secondary:hover {
|
||||
background-color: white;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.headline {
|
||||
font-size: 2.4rem;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 1.05rem;
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
170
src/components/main/Menu.vue
Normal file
@ -0,0 +1,170 @@
|
||||
<template>
|
||||
<section id="menu" class="featured">
|
||||
<h2>Popular Dishes</h2>
|
||||
<div class="dishes">
|
||||
<div
|
||||
v-for="dish in dishes"
|
||||
:key="dish.id"
|
||||
class="dish"
|
||||
>
|
||||
<img
|
||||
:src="dish.image"
|
||||
width="280"
|
||||
height="280"
|
||||
:alt="dish.alt"
|
||||
/>
|
||||
<h3>{{ dish.name }}</h3>
|
||||
<p>{{ dish.description }}</p>
|
||||
<div class="card-actions">
|
||||
<a
|
||||
:href="dish.whatsappLink"
|
||||
class="whatsapp-button"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
>
|
||||
WhatsApp
|
||||
</a>
|
||||
<button
|
||||
class="detail-button"
|
||||
type="button"
|
||||
>
|
||||
{{ dish.detailLabel }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import spaghettiImg from "@/assets/spaghetti.jpg";
|
||||
import steakImg from "@/assets/steak.jpg";
|
||||
import saladImg from "@/assets/salad.jpg";
|
||||
|
||||
const whatsappLink = "https://wa.me/1234567890";
|
||||
|
||||
const dishes = [
|
||||
{
|
||||
id: "spaghetti",
|
||||
name: "Spaghetti Carbonara",
|
||||
description: "Classic Italian pasta with creamy sauce and pancetta.",
|
||||
image: spaghettiImg,
|
||||
alt: "Spaghetti Carbonara",
|
||||
whatsappLink,
|
||||
detailLabel: "Detail"
|
||||
},
|
||||
{
|
||||
id: "steak",
|
||||
name: "Grilled Steak",
|
||||
description: "Perfectly grilled steak served with mashed potatoes.",
|
||||
image: steakImg,
|
||||
alt: "Grilled Steak",
|
||||
whatsappLink,
|
||||
detailLabel: "Detail"
|
||||
},
|
||||
{
|
||||
id: "salad",
|
||||
name: "Caesar Salad",
|
||||
description: "Fresh greens tossed in Caesar dressing.",
|
||||
image: saladImg,
|
||||
alt: "Caesar Salad",
|
||||
whatsappLink,
|
||||
detailLabel: "Detail"
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#menu {
|
||||
box-sizing: border-box;
|
||||
padding: 100px 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
text-align: center;
|
||||
margin-bottom: 50px;
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.dishes {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 0 80px;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.dish {
|
||||
background-color: white;
|
||||
width: 280px;
|
||||
height: 400px;
|
||||
border-radius: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
box-shadow: 0 0 13px 0 rgba(0, 0, 0, 0.3);
|
||||
transition: 300ms;
|
||||
padding: 0 0 20px;
|
||||
}
|
||||
|
||||
.dish:hover {
|
||||
margin-top: -10px;
|
||||
box-shadow: 0 0 20px 1px rgba(0, 0, 0, 1);
|
||||
}
|
||||
|
||||
img {
|
||||
border-radius: 20px 20px 0 0;
|
||||
height: 220px;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
p {
|
||||
width: 230px;
|
||||
}
|
||||
|
||||
.card-actions {
|
||||
margin-top: auto;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 230px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.detail-button {
|
||||
background-color: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 6px 12px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.detail-button:hover {
|
||||
background-color: #0056b3;
|
||||
}
|
||||
|
||||
.whatsapp-button {
|
||||
background-color: #25d366;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
padding: 6px 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.whatsapp-button:hover {
|
||||
background-color: #1ebe5d;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.dishes {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.dish {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
53
src/components/main/ReasonsSection.vue
Normal file
@ -0,0 +1,53 @@
|
||||
<script setup>
|
||||
const steps = [
|
||||
{
|
||||
number: '1.',
|
||||
title: 'Konsultasi & Pemilihan Layanan',
|
||||
description: 'Pilih layanan yang sesuai dengan kebutuhan usaha Anda.'
|
||||
},
|
||||
{
|
||||
number: '2.',
|
||||
title: 'Upload & Verifikasi Dokumen',
|
||||
description: 'Kirim dokumen yang dibutuhkan, kami bantu cek kelengkapannya.'
|
||||
},
|
||||
{
|
||||
number: '3.',
|
||||
title: 'Proses oleh Tim Legal',
|
||||
description: 'Dokumen Anda diurus oleh notaris & konsultan hukum terpercaya.'
|
||||
},
|
||||
{
|
||||
number: '4.',
|
||||
title: 'Dokumen Selesai & Dikirim',
|
||||
description: 'Terima semua dokumen resmi dalam bentuk digital dan fisik.'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="reasons">
|
||||
<div class="container">
|
||||
<h2 class="headline">Bagaimana Kami Membantu Anda</h2>
|
||||
<div class="grid">
|
||||
<article v-for="(step, i) in steps" :key="i" class="card">
|
||||
<div class="number-badge">{{ step.number }}</div>
|
||||
<h3 class="card-title">{{ step.title }}</h3>
|
||||
<p class="card-desc">{{ step.description }}</p>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.reasons { padding: 80px 20px; background: #f9fafb; }
|
||||
.container { max-width: 1200px; margin: 0 auto; }
|
||||
.headline { font-size: 36px; font-weight: 800; text-align: center; margin: 0 0 56px; color: #111827; }
|
||||
.grid { display: grid; gap: 24px; grid-template-columns: 1fr; }
|
||||
@media (min-width: 640px) { .grid { grid-template-columns: repeat(2, 1fr); } }
|
||||
@media (min-width: 1024px) { .grid { grid-template-columns: repeat(4, 1fr); } }
|
||||
.card { background: #fff; padding: 32px 24px; box-shadow: 0 1px 3px rgba(0,0,0,0.1); text-align: left; }
|
||||
.number-badge { width: 48px; height: 48px; border-radius: 50%; border: 2px solid #e5e7eb; display: flex; align-items: center; justify-content: center; font-size: 18px; font-weight: 700; color: #6b7280; margin-bottom: 16px; }
|
||||
.card-title { font-size: 18px; font-weight: 700; margin: 0 0 12px; color: #111827; line-height: 1.4; }
|
||||
.card-desc { font-size: 14px; line-height: 1.6; color: #6b7280; margin: 0; }
|
||||
@media (min-width: 768px) { .headline { font-size: 40px; } }
|
||||
</style>
|
||||
192
src/components/main/Service.vue
Normal file
@ -0,0 +1,192 @@
|
||||
<template>
|
||||
<div class="service-card">
|
||||
<div class="diamond-wrapper">
|
||||
<div class="diamond-bg"></div>
|
||||
<img
|
||||
:src="image"
|
||||
:alt="title"
|
||||
class="service-image"
|
||||
/>
|
||||
<div class="icon-badge">
|
||||
<svg v-if="!primary" width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M19 3H5C3.89543 3 3 3.89543 3 5V19C3 20.1046 3.89543 21 5 21H19C20.1046 21 21 20.1046 21 19V5C21 3.89543 20.1046 3 19 3Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M9 9H15" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M9 13H15" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M9 17H13" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
<svg v-else width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M21 15C21 15.5304 20.7893 16.0391 20.4142 16.4142C20.0391 16.7893 19.5304 17 19 17H7L3 21V5C3 4.46957 3.21071 3.96086 3.58579 3.58579C3.96086 3.21071 4.46957 3 5 3H19C19.5304 3 20.0391 3.21071 20.4142 3.58579C20.7893 3.96086 21 4.46957 21 5V15Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<!-- Tags -->
|
||||
<div v-if="tags && tags.length > 0" class="tags">
|
||||
<div v-for="(tag, i) in tags" :key="i" class="tag">
|
||||
{{ tag }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 class="service-title">{{ title }}</h3>
|
||||
|
||||
<p class="service-desc">
|
||||
{{ description }}
|
||||
</p>
|
||||
|
||||
<button
|
||||
class="service-btn"
|
||||
:class="{ primary }"
|
||||
type="button"
|
||||
>
|
||||
{{ buttonText }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const { image, title, description, buttonText, primary, tags } = defineProps({
|
||||
image: { type: String, required: true },
|
||||
title: { type: String, required: true },
|
||||
description: { type: String, required: true },
|
||||
buttonText: { type: String, required: true },
|
||||
primary: { type: Boolean, default: false },
|
||||
tags: { type: Array, default: () => [] }
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.service-card {
|
||||
background: #f9fafb;
|
||||
padding: 32px 24px;
|
||||
text-align: center;
|
||||
transition: 0.2s ease-in-out;
|
||||
cursor: default;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.service-card:hover {
|
||||
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* Diamond wrapper */
|
||||
.diamond-wrapper {
|
||||
position: relative;
|
||||
width: 260px;
|
||||
height: 240px;
|
||||
margin-bottom: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.diamond-bg {
|
||||
position: absolute;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
background: linear-gradient(135deg, #FF6640 0%, #e85a30 100%);
|
||||
transform: rotate(45deg);
|
||||
z-index: 1;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.service-image {
|
||||
position: relative;
|
||||
width: 180px;
|
||||
height: 200px;
|
||||
object-fit: cover;
|
||||
object-position: center top;
|
||||
z-index: 2;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.icon-badge {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 20px;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background: #fff;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 2px 12px rgba(0,0,0,0.15);
|
||||
z-index: 3;
|
||||
color: #FF6640;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
.tags {
|
||||
position: absolute;
|
||||
bottom: -8px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
z-index: 3;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
max-width: 220px;
|
||||
}
|
||||
|
||||
.tag {
|
||||
background: rgba(75, 85, 99, 0.88);
|
||||
color: #fff;
|
||||
padding: 7px 14px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.25);
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
/* Title */
|
||||
.service-title {
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 12px;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
/* Description */
|
||||
.service-desc {
|
||||
font-size: 14px;
|
||||
color: #6b7280;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 24px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* Button */
|
||||
.service-btn {
|
||||
border: 2px solid #FF6640;
|
||||
padding: 12px 28px;
|
||||
color: #FF6640;
|
||||
font-weight: 600;
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
transition: 0.2s;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.service-btn:hover {
|
||||
background: #FF6640;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Primary style */
|
||||
.service-btn.primary {
|
||||
background: #FF6640;
|
||||
color: white;
|
||||
border-color: #FF6640;
|
||||
}
|
||||
|
||||
.service-btn.primary:hover {
|
||||
background: #e85a30;
|
||||
border-color: #e85a30;
|
||||
}
|
||||
</style>
|
||||
118
src/components/main/ServiceSection.vue
Normal file
@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<section class="services-wrapper">
|
||||
<div class="container">
|
||||
<div class="eyebrow">SOLUSI LEGAL</div>
|
||||
<h2 class="headline">Semua Urusan Legal Anda,<br/>Kami Bantu Selesaikan</h2>
|
||||
<p class="description">
|
||||
Solnus membantu individu dan perusahaan mengurusberbagai kebutuhan hukum, dari pendirian badan usaha, pengurusan izin, hingga konsultasi legal. Kami menghadirkan kemudahan, transparansi, dan kepastian dalam setiap proses legal yang Anda butuhkan.
|
||||
</p>
|
||||
|
||||
<div class="services-container">
|
||||
<Service
|
||||
v-for="service in services"
|
||||
:key="service.id"
|
||||
:image="service.image"
|
||||
:title="service.title"
|
||||
:description="service.description"
|
||||
:button-text="service.buttonText"
|
||||
:primary="service.primary"
|
||||
:tags="service.tags"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Service from "./Service.vue";
|
||||
|
||||
const services = [
|
||||
{
|
||||
id: "business-formation",
|
||||
image: 'https://images.unsplash.com/photo-1560250097-0b93528c311a?w=400&h=400&fit=crop',
|
||||
title: "Pendirian Badan Usaha",
|
||||
description: "Buat PT, CV, atau Yayasan secara online, dengan dokumen resmi Kemenkumham.",
|
||||
buttonText: "Lihat Detail Layanan",
|
||||
primary: false,
|
||||
tags: ['PT', 'CV', 'Yayasan']
|
||||
},
|
||||
{
|
||||
id: "license-nib",
|
||||
image: 'https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?w=400&h=400&fit=crop',
|
||||
title: "Perizinan & NIB",
|
||||
description: "Dapatkan Nomor Induk Berusaha (NIB) dan izin OSS dengan cepat & aman.",
|
||||
buttonText: "Lihat Detail Layanan",
|
||||
primary: false,
|
||||
tags: []
|
||||
},
|
||||
{
|
||||
id: "legal-consultation",
|
||||
image: 'https://images.unsplash.com/photo-1519085360753-af0119f7cbe7?w=400&h=400&fit=crop',
|
||||
title: "Konsultasi Hukum",
|
||||
description: "Konsultasikan masalah hukum bisnis Anda dengan ahli berpengalaman.",
|
||||
buttonText: "Jadwalkan Konsultasi Gratis",
|
||||
primary: true,
|
||||
tags: ['Lebih baik PT atau CV ya?', 'Ini masih bisa daftar NIB?']
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.services-wrapper {
|
||||
padding: 80px 20px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: #FF6640;
|
||||
letter-spacing: 1px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.headline {
|
||||
font-size: 36px;
|
||||
font-weight: 800;
|
||||
text-align: center;
|
||||
color: #111827;
|
||||
margin: 0 0 20px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 15px;
|
||||
line-height: 1.7;
|
||||
color: #6b7280;
|
||||
text-align: center;
|
||||
max-width: 800px;
|
||||
margin: 0 auto 56px;
|
||||
}
|
||||
|
||||
.services-container {
|
||||
display: grid;
|
||||
gap: 32px;
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.services-container {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
.headline {
|
||||
font-size: 42px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.headline {
|
||||
font-size: 28px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
159
src/components/main/ServicesMenu.vue
Normal file
@ -0,0 +1,159 @@
|
||||
<script setup>
|
||||
const services = [
|
||||
{
|
||||
id: 'pendirian',
|
||||
title: 'Paket Pendirian',
|
||||
icon: '🏢',
|
||||
image: 'https://images.unsplash.com/photo-1486406146926-c627a92ad1ab?w=400&h=300&fit=crop'
|
||||
},
|
||||
{
|
||||
id: 'perubahan',
|
||||
title: 'Paket Perubahan\n& Pembubaran',
|
||||
icon: '📄',
|
||||
image: 'https://images.unsplash.com/photo-1450101499163-c8848c66ca85?w=400&h=300&fit=crop'
|
||||
},
|
||||
{
|
||||
id: 'property',
|
||||
title: 'Paket Property',
|
||||
icon: '🏠',
|
||||
image: 'https://images.unsplash.com/photo-1560518883-ce09059eeffa?w=400&h=300&fit=crop'
|
||||
},
|
||||
{
|
||||
id: 'khusus',
|
||||
title: 'Paket Khusus',
|
||||
icon: '⚖️',
|
||||
image: 'https://images.unsplash.com/photo-1589829545856-d10d557cf95f?w=400&h=300&fit=crop'
|
||||
},
|
||||
{
|
||||
id: 'umum',
|
||||
title: 'Paket Umum',
|
||||
icon: '🔨',
|
||||
image: 'https://images.unsplash.com/photo-1505664194779-8beaceb93744?w=400&h=300&fit=crop'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="services-menu">
|
||||
<div class="overlay"></div>
|
||||
<div class="container">
|
||||
<h2 class="headline">Layanan Legal Solnus</h2>
|
||||
<div class="cards">
|
||||
<a
|
||||
v-for="service in services"
|
||||
:key="service.id"
|
||||
:href="`#${service.id}`"
|
||||
class="card"
|
||||
>
|
||||
<div class="card-icon">{{ service.icon }}</div>
|
||||
<h3 class="card-title">{{ service.title }}</h3>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.services-menu {
|
||||
position: relative;
|
||||
padding: 80px 20px;
|
||||
background-image: url('@/assets/service_legal.jpg');
|
||||
background-size: cover;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.75);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.headline {
|
||||
font-size: 36px;
|
||||
font-weight: 800;
|
||||
text-align: center;
|
||||
margin: 0 0 48px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.cards {
|
||||
display: grid;
|
||||
gap: 20px;
|
||||
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.cards {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.cards {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.cards {
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
padding: 32px 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
text-decoration: none;
|
||||
transition: transform .2s, box-shadow .2s;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
font-size: 48px;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #f9fafb;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: #111827;
|
||||
text-align: center;
|
||||
line-height: 1.4;
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.headline {
|
||||
font-size: 42px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
92
src/components/main/Testimonial.vue
Normal file
@ -0,0 +1,92 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const testimonials = [
|
||||
{
|
||||
name: 'Imam Santosa',
|
||||
role: 'Owner Nona Craft Studio',
|
||||
text: 'Saya sempat ragu bikin PT karena takut ribet dan mahal. Tapi ternyata lewat Solnus, semua prosesnya transparan, cepat, dan saya bisa pantau dari HP. Dalam 7 hari, SK dan NIB saya sudah keluar. Sekarang bisnis saya bisa kerja sama dengan brand besar.',
|
||||
avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=200&h=200&fit=crop'
|
||||
}
|
||||
]
|
||||
|
||||
const current = ref(0)
|
||||
|
||||
function prev() {
|
||||
current.value = (current.value - 1 + testimonials.length) % testimonials.length
|
||||
}
|
||||
|
||||
function next() {
|
||||
current.value = (current.value + 1) % testimonials.length
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="testimonial">
|
||||
<div class="container">
|
||||
<div class="eyebrow">1500+ Pengusaha Telah Buktikan</div>
|
||||
<h2 class="headline">Sekarang Giliran Anda!</h2>
|
||||
|
||||
<div class="card">
|
||||
<div class="author">
|
||||
<img :src="testimonials[current].avatar" :alt="testimonials[current].name" class="avatar" />
|
||||
<div class="meta">
|
||||
<div class="name">{{ testimonials[current].name }}</div>
|
||||
<div class="role">{{ testimonials[current].role }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<blockquote class="quote">
|
||||
"{{ testimonials[current].text }}"
|
||||
</blockquote>
|
||||
|
||||
<div class="stats">
|
||||
<div class="stat">
|
||||
<div class="stat-value">100%</div>
|
||||
<div class="stat-label">Kepuasan pelanggan</div>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<div class="stat-value">4x</div>
|
||||
<div class="stat-label">Lebih cepat dari proses manual</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="nav">
|
||||
<button @click="prev" class="nav-btn" aria-label="Previous">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.5 15L7.5 10L12.5 5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</button>
|
||||
<button @click="next" class="nav-btn" aria-label="Next">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.5 15L12.5 10L7.5 5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.testimonial { padding: 80px 20px; background: #fff; }
|
||||
.container { max-width: 1200px; margin: 0 auto; }
|
||||
.eyebrow { font-size: 16px; font-weight: 600; color: #FF6640; text-align: center; margin: 0 0 12px; letter-spacing: .5px; }
|
||||
.headline { font-size: 40px; font-weight: 800; text-align: center; margin: 0 0 56px; color: #111827; line-height: 1.2; }
|
||||
.card { padding: 48px; background: #f9fafb; border-radius: 16px; position: relative; max-width: 1000px; margin: 0 auto; }
|
||||
.author { display: flex; align-items: center; gap: 16px; margin-bottom: 24px; }
|
||||
.avatar { width: 64px; height: 64px; border-radius: 50%; object-fit: cover; }
|
||||
.meta { display: flex; flex-direction: column; gap: 4px; }
|
||||
.name { font-weight: 700; font-size: 18px; color: #111827; }
|
||||
.role { font-size: 15px; color: #9ca3af; font-style: italic; }
|
||||
.quote { margin: 0 0 32px; font-size: 20px; line-height: 1.7; color: #374151; font-weight: 400; }
|
||||
.stats { display: flex; gap: 48px; margin-bottom: 0; }
|
||||
.stat { }
|
||||
.stat-value { font-size: 28px; font-weight: 800; color: #111827; margin-bottom: 4px; }
|
||||
.stat-label { font-size: 14px; color: #6b7280; }
|
||||
.nav { position: absolute; bottom: 48px; right: 48px; display: flex; gap: 12px; }
|
||||
.nav-btn { width: 44px; height: 44px; border-radius: 50%; border: 1px solid #e5e7eb; background: #fff; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all .2s; color: #6b7280; }
|
||||
.nav-btn:hover { background: #f3f4f6; border-color: #d1d5db; color: #111827; }
|
||||
@media (min-width: 768px) { .headline { font-size: 48px; } }
|
||||
@media (max-width: 768px) { .card { padding: 32px 24px; } .nav { position: static; margin-top: 24px; justify-content: center; } .stats { flex-direction: column; gap: 20px; } }
|
||||
</style>
|
||||
67
src/components/main/WhyChoose.vue
Normal file
@ -0,0 +1,67 @@
|
||||
<script setup>
|
||||
const features = [
|
||||
{
|
||||
icon: 'lightning',
|
||||
title: 'Proses Cepat & Efisien',
|
||||
description: 'Semua layanan dilakukan online dengan deadline jelas untuk efisiensi.'
|
||||
},
|
||||
{
|
||||
icon: 'lock',
|
||||
title: 'Aman & Terpercaya',
|
||||
description: 'Dokumen diverifikasi langsung oleh notaris dan instansi resmi.'
|
||||
},
|
||||
{
|
||||
icon: 'star',
|
||||
title: 'Tim Profesional',
|
||||
description: 'Didukung pengacara dan konsultan hukum berpengalaman.'
|
||||
},
|
||||
{
|
||||
icon: 'message',
|
||||
title: 'Proses Transparan',
|
||||
description: 'Anda bisa pantau status dokumen kapan pun.'
|
||||
}
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="why-choose">
|
||||
<div class="container">
|
||||
<h2 class="headline">Kenapa Bisnis Memilih Solusi Kami</h2>
|
||||
<div class="grid">
|
||||
<article v-for="(feature, i) in features" :key="i" class="card">
|
||||
<div class="icon-wrapper">
|
||||
<svg v-if="feature.icon === 'lightning'" width="32" height="32" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13 2L3 14H12L11 22L21 10H12L13 2Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
<svg v-else-if="feature.icon === 'lock'" width="32" height="32" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="3" y="11" width="18" height="11" rx="2" stroke="currentColor" stroke-width="2"/>
|
||||
<path d="M7 11V7C7 4.79086 8.79086 3 11 3H13C15.2091 3 17 4.79086 17 7V11" stroke="currentColor" stroke-width="2"/>
|
||||
</svg>
|
||||
<svg v-else-if="feature.icon === 'star'" width="32" height="32" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12 2L15.09 8.26L22 9.27L17 14.14L18.18 21.02L12 17.77L5.82 21.02L7 14.14L2 9.27L8.91 8.26L12 2Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
<svg v-else-if="feature.icon === 'message'" width="32" height="32" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M21 11.5C21.0034 12.8199 20.6951 14.1219 20.1 15.3C19.3944 16.7118 18.3098 17.8992 16.9674 18.7293C15.6251 19.5594 14.0782 19.9994 12.5 20C11.1801 20.0035 9.87812 19.6951 8.7 19.1L3 21L4.9 15.3C4.30493 14.1219 3.99656 12.8199 4 11.5C4.00061 9.92179 4.44061 8.37488 5.27072 7.03258C6.10083 5.69028 7.28825 4.6056 8.7 3.90003C9.87812 3.30496 11.1801 2.99659 12.5 3.00003H13C15.0843 3.11502 17.053 3.99479 18.5291 5.47089C20.0052 6.94699 20.885 8.91568 21 11V11.5Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
</div>
|
||||
<h3 class="card-title">{{ feature.title }}</h3>
|
||||
<p class="card-desc">{{ feature.description }}</p>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.why-choose { padding: 80px 20px; background: #111827; }
|
||||
.container { max-width: 1200px; margin: 0 auto; }
|
||||
.headline { font-size: 36px; font-weight: 800; text-align: center; margin: 0 0 56px; color: #fff; line-height: 1.2; }
|
||||
.grid { display: grid; gap: 32px; grid-template-columns: 1fr; }
|
||||
@media (min-width: 640px) { .grid { grid-template-columns: repeat(2, 1fr); } }
|
||||
@media (min-width: 1024px) { .grid { grid-template-columns: repeat(4, 1fr); } }
|
||||
.card { text-align: center; }
|
||||
.icon-wrapper { width: 64px; height: 64px; margin: 0 auto 20px; display: flex; align-items: center; justify-content: center; border-radius: 50%; border: 2px solid rgba(255,255,255,0.2); color: #fff; }
|
||||
.card-title { font-size: 18px; font-weight: 700; margin: 0 0 12px; color: #fff; }
|
||||
.card-desc { font-size: 14px; line-height: 1.6; color: #9ca3af; margin: 0; }
|
||||
@media (min-width: 768px) { .headline { font-size: 40px; } }
|
||||
</style>
|
||||
60
src/components/packages/PackageCard.vue
Normal file
@ -0,0 +1,60 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
title: { type: String, required: true },
|
||||
price: { type: String, required: true },
|
||||
subtitle: { type: String, default: '' },
|
||||
image: { type: String, default: '' },
|
||||
features: { type: Array, default: () => [] },
|
||||
ctaText: { type: String, default: 'Jadwalkan Konsultasi Gratis' }
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<article class="card">
|
||||
<div class="media" v-if="image">
|
||||
<img :src="image" :alt="title" />
|
||||
<div class="overlay"></div>
|
||||
<div class="media-text">
|
||||
<h3>{{ title }}</h3>
|
||||
<p>{{ subtitle }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="body">
|
||||
<div class="price">{{ price }}</div>
|
||||
<ul class="features">
|
||||
<li v-for="(f, i) in features" :key="i">
|
||||
<svg class="icon" width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="8" cy="8" r="8" fill="#10b981"/>
|
||||
<path d="M11 6L7 10L5 8" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
<span>{{ f }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<button class="cta">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="margin-right: 6px;">
|
||||
<path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413Z" fill="currentColor"/>
|
||||
</svg>
|
||||
{{ ctaText }}
|
||||
</button>
|
||||
</div>
|
||||
</article>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.card { border: 1px solid #e5e7eb; border-radius: 12px; overflow: hidden; background: #fff; display: flex; flex-direction: column; height: 100%; box-shadow: 0 1px 3px rgba(0,0,0,.08); transition: box-shadow .2s; }
|
||||
.card:hover { box-shadow: 0 4px 12px rgba(0,0,0,.12); }
|
||||
.media { position: relative; height: 160px; overflow: hidden; }
|
||||
.media img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
||||
.overlay { position: absolute; inset: 0; background: linear-gradient(180deg, rgba(0,0,0,0.6) 0%, rgba(0,0,0,0.3) 100%); }
|
||||
.media-text { position: absolute; bottom: 12px; left: 16px; right: 16px; color: #fff; }
|
||||
.media-text h3 { margin: 0; font-size: 18px; font-weight: 700; }
|
||||
.media-text p { margin: 4px 0 0; opacity: 0.95; font-size: 13px; }
|
||||
.body { padding: 20px; display: flex; flex-direction: column; gap: 16px; flex: 1; }
|
||||
.price { font-weight: 800; color: #EE3000; font-size: 24px; }
|
||||
.features { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; flex: 1; }
|
||||
.features li { display: flex; align-items: flex-start; gap: 10px; color: #374151; font-size: 14px; line-height: 1.5; }
|
||||
.icon { flex-shrink: 0; margin-top: 2px; }
|
||||
.cta { width: 100%; padding: 12px 16px; background: #111827; color: #fff; border: none; border-radius: 8px; cursor: pointer; font-weight: 600; font-size: 14px; display: flex; align-items: center; justify-content: center; transition: background .2s; }
|
||||
.cta:hover { background: #000; }
|
||||
</style>
|
||||
29
src/components/packages/PackagesGrid.vue
Normal file
@ -0,0 +1,29 @@
|
||||
<script setup>
|
||||
import PackageCard from './PackageCard.vue'
|
||||
|
||||
const props = defineProps({
|
||||
items: { type: Array, default: () => [] }
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="grid">
|
||||
<PackageCard
|
||||
v-for="(p, i) in items"
|
||||
:key="i"
|
||||
:title="p.title"
|
||||
:subtitle="p.subtitle"
|
||||
:price="p.price"
|
||||
:image="p.image"
|
||||
:features="p.features"
|
||||
:cta-text="p.ctaText"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.grid { display: grid; grid-template-columns: repeat(12, 1fr); gap: 16px; }
|
||||
@media (min-width: 0px) { .grid { grid-template-columns: 1fr; } }
|
||||
@media (min-width: 640px) { .grid { grid-template-columns: repeat(2, 1fr); } }
|
||||
@media (min-width: 1024px) { .grid { grid-template-columns: repeat(3, 1fr); } }
|
||||
</style>
|
||||
44
src/components/packages/PackagesSection.vue
Normal file
@ -0,0 +1,44 @@
|
||||
<script setup>
|
||||
import { TABS as tabs, DATA_BY_TAB as dataByTab } from './data.js'
|
||||
import { computed } from 'vue'
|
||||
import PackagesGrid from './PackagesGrid.vue'
|
||||
|
||||
const props = defineProps({
|
||||
initialCategory: { type: String, default: null }
|
||||
})
|
||||
|
||||
// If no category specified, show all packages
|
||||
const currentCategory = computed(() => props.initialCategory || null)
|
||||
|
||||
const currentData = computed(() => {
|
||||
if (!currentCategory.value) {
|
||||
// Show all packages from all categories
|
||||
return Object.values(dataByTab).flat()
|
||||
}
|
||||
return dataByTab[currentCategory.value] || []
|
||||
})
|
||||
|
||||
const headline = computed(() => {
|
||||
if (!currentCategory.value) {
|
||||
return 'Semua Paket Layanan'
|
||||
}
|
||||
const tab = tabs.find(t => t.key === currentCategory.value)
|
||||
return tab ? tab.label : 'Paket Layanan'
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="packages">
|
||||
<div class="container">
|
||||
<h2 class="headline">{{ headline }}</h2>
|
||||
<PackagesGrid :items="currentData" />
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.packages { padding: 80px 0; background: #fff; margin-top: 60px; }
|
||||
.container { max-width: 1200px; margin: 0 auto; padding: 0 20px; }
|
||||
.headline { font-size: 32px; font-weight: 800; text-align: center; margin: 0 0 40px; color: #111827; }
|
||||
@media (min-width: 768px) { .headline { font-size: 40px; margin-bottom: 48px; } }
|
||||
</style>
|
||||
169
src/components/packages/data.js
Normal file
@ -0,0 +1,169 @@
|
||||
export const TABS = [
|
||||
{ key: 'pendirian', label: 'Paket Pendirian' },
|
||||
{ key: 'perubahan', label: 'Paket Perubahan & Pembubaran' },
|
||||
{ key: 'property', label: 'Paket Property' },
|
||||
{ key: 'khusus', label: 'Paket Khusus' },
|
||||
{ key: 'umum', label: 'Paket Umum' }
|
||||
]
|
||||
|
||||
const commonFeatures = [
|
||||
'Akta Pendirian',
|
||||
'SK Pendirian AHU (Kemenkum)',
|
||||
'NIB',
|
||||
'Akun OSS',
|
||||
'K3L',
|
||||
'SPPL',
|
||||
'Pernyataan Mandiri',
|
||||
'Sertifikat Standar (untuk KBLI tertentu)',
|
||||
'NPWP',
|
||||
'Akun Coretax',
|
||||
'SKT Pajak',
|
||||
'Surat Penerbitan Akun Pajak',
|
||||
'Bukti Penerimaan Surat Pendaftaran Wajib Pajak',
|
||||
'Email Badan',
|
||||
'Stempel/Cap Badan',
|
||||
'Free Pengecekan Nama',
|
||||
'Free Konsultasi'
|
||||
]
|
||||
|
||||
const img = (q) => `https://images.unsplash.com/${q}&auto=format&fit=crop&w=1200&q=60`
|
||||
|
||||
export const DATA_BY_TAB = {
|
||||
pendirian: [
|
||||
{
|
||||
title: 'Pendirian PT',
|
||||
subtitle: 'Mulai Langkah Pertama Bisnis Resmi Anda',
|
||||
price: 'Rp 3,850 K',
|
||||
image: img('photo-1520975916090-3105956dac38?ixlib=rb-4.0.3'),
|
||||
features: commonFeatures
|
||||
},
|
||||
{
|
||||
title: 'Paket Level Up',
|
||||
subtitle: 'Saatnya Naik Kelas & Siap Dapat Klien Besar',
|
||||
price: 'Rp 3,200 K',
|
||||
image: img('photo-1517245386807-bb43f82c33c4?ixlib=rb-4.0.3'),
|
||||
features: commonFeatures
|
||||
},
|
||||
{
|
||||
title: 'Pendirian Yayasan',
|
||||
subtitle: 'Yayasan Resmi, Siap Operasional Penuh',
|
||||
price: 'Rp 3,800 K',
|
||||
image: img('photo-1515165562835-c3b8b2c9288b?ixlib=rb-4.0.3'),
|
||||
features: commonFeatures
|
||||
},
|
||||
{
|
||||
title: 'Pendirian Firma',
|
||||
subtitle: 'Mulai Langkah Pertama Bisnis Resmi Anda',
|
||||
price: 'Rp 3,500 K',
|
||||
image: img('photo-1525182008055-f88b95ff7980?ixlib=rb-4.0.3'),
|
||||
features: commonFeatures
|
||||
},
|
||||
{
|
||||
title: 'Pendirian Perkumpulan',
|
||||
subtitle: 'Mulai Langkah Pertama Bisnis Resmi Anda',
|
||||
price: 'Rp 5,000 K',
|
||||
image: img('photo-1521737604893-d14cc237f11d?ixlib=rb-4.0.3'),
|
||||
features: commonFeatures
|
||||
},
|
||||
{
|
||||
title: 'Pendirian Persekutuan Perdata',
|
||||
subtitle: 'Mulai Langkah Pertama Bisnis Resmi Anda',
|
||||
price: 'Rp 3,500 K',
|
||||
image: img('photo-1538688423619-a81d3f23454b?ixlib=rb-4.0.3'),
|
||||
features: commonFeatures
|
||||
},
|
||||
{
|
||||
title: 'Pendirian Koperasi',
|
||||
subtitle: 'Mulai Langkah Pertama Bisnis Resmi Anda',
|
||||
price: 'Rp 8,500 K',
|
||||
image: img('photo-1520974722070-6a669e3f0de7?ixlib=rb-4.0.3'),
|
||||
features: commonFeatures
|
||||
},
|
||||
{
|
||||
title: 'Pendirian PT Perseorangan',
|
||||
subtitle: 'Mulai Langkah Pertama Bisnis Resmi Anda',
|
||||
price: 'Rp 1,200 K',
|
||||
image: img('photo-1519340241574-2cec6aef0c01?ixlib=rb-4.0.3'),
|
||||
features: [
|
||||
'Sertifikat Pendaftaran Pendirian Perseorangan (Kemenkum)',
|
||||
'Pernyataan Pendirian Kemenkum',
|
||||
'NIB', 'Akun OSS', 'K3L', 'SPPL', 'Pernyataan Mandiri',
|
||||
'Sertifikat Standar (untuk KBLI tertentu)', 'NPWP', 'Akun Coretax', 'SKT Pajak',
|
||||
'Surat Penerbitan Akun Pajak', 'Bukti Penerimaan Surat Pendaftaran Wajib Pajak',
|
||||
'Email Badan', 'Stempel/Cap Badan', 'Free Pengecekan Nama', 'Free Konsultasi'
|
||||
]
|
||||
}
|
||||
],
|
||||
perubahan: [
|
||||
{
|
||||
title: 'Perubahan Data PT',
|
||||
subtitle: 'Ubah nama, alamat, modal, pengurus',
|
||||
price: 'Konsultasi Dulu',
|
||||
image: img('photo-1522075469751-3a6694fb2f61?ixlib=rb-4.0.3'),
|
||||
features: [
|
||||
'Analisis kebutuhan perubahan',
|
||||
'Penyusunan akta perubahan',
|
||||
'Pengesahan AHU (jika perlu)',
|
||||
'Update OSS dan perpajakan',
|
||||
'Dokumentasi lengkap',
|
||||
'Free Konsultasi'
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Pembubaran Badan Usaha',
|
||||
subtitle: 'Aman sesuai regulasi',
|
||||
price: 'Konsultasi Dulu',
|
||||
image: img('photo-1521791136064-7986c2920216?ixlib=rb-4.0.3'),
|
||||
features: [
|
||||
'Konsultasi dan strategi pembubaran',
|
||||
'Penyusunan akta pembubaran',
|
||||
'Pencabutan perizinan terkait',
|
||||
'Penutupan perpajakan (jika perlu)',
|
||||
'Dokumentasi lengkap',
|
||||
'Free Konsultasi'
|
||||
]
|
||||
}
|
||||
],
|
||||
property: [
|
||||
{
|
||||
title: 'Legalitas Property Dasar',
|
||||
subtitle: 'Cek sertifikat dan dokumen',
|
||||
price: 'Konsultasi Dulu',
|
||||
image: img('photo-1505693416388-ac5ce068fe85?ixlib=rb-4.0.3'),
|
||||
features: [
|
||||
'Review dokumen kepemilikan',
|
||||
'Cek keabsahan sertifikat',
|
||||
'Rekomendasi langkah lanjutan',
|
||||
'Free Konsultasi'
|
||||
]
|
||||
}
|
||||
],
|
||||
khusus: [
|
||||
{
|
||||
title: 'Paket Khusus Tender',
|
||||
subtitle: 'Lengkap untuk kebutuhan pengadaan',
|
||||
price: 'Customize',
|
||||
image: img('photo-1542744173-8e7e53415bb0?ixlib=rb-4.0.3'),
|
||||
features: [
|
||||
'Dokumen administrasi lengkap',
|
||||
'Perizinan spesifik KBLI',
|
||||
'Pendampingan kelengkapan dokumen',
|
||||
'Free Konsultasi'
|
||||
]
|
||||
}
|
||||
],
|
||||
umum: [
|
||||
{
|
||||
title: 'Virtual Office + Legal',
|
||||
subtitle: 'Hemat dan fleksibel',
|
||||
price: 'Start from Rp 1 Jt',
|
||||
image: img('photo-1487014679447-9f8336841d58?ixlib=rb-4.0.3'),
|
||||
features: [
|
||||
'Alamat legal bisnis',
|
||||
'Forwarding surat',
|
||||
'Konsultasi legal dasar',
|
||||
'Free Konsultasi'
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
72
src/components/ui/Tabs.vue
Normal file
@ -0,0 +1,72 @@
|
||||
<script setup>
|
||||
import { computed, ref, watch } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
tabs: { type: Array, required: true },
|
||||
modelValue: { type: String, default: '' },
|
||||
ariaLabel: { type: String, default: 'Sections' }
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'change'])
|
||||
|
||||
const internal = ref(props.modelValue || (props.tabs[0] && props.tabs[0].key) || '')
|
||||
|
||||
watch(() => props.modelValue, (v) => {
|
||||
if (v && v !== internal.value) internal.value = v
|
||||
})
|
||||
|
||||
watch(internal, (v) => {
|
||||
emit('update:modelValue', v)
|
||||
emit('change', v)
|
||||
})
|
||||
|
||||
const activeIndex = computed(() => props.tabs.findIndex(t => t.key === internal.value))
|
||||
|
||||
function selectByIndex(idx) {
|
||||
if (idx < 0 || idx >= props.tabs.length) return
|
||||
internal.value = props.tabs[idx].key
|
||||
}
|
||||
|
||||
function onKeydown(e) {
|
||||
if (!['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(e.key)) return
|
||||
e.preventDefault()
|
||||
if (e.key === 'ArrowLeft') selectByIndex((activeIndex.value - 1 + props.tabs.length) % props.tabs.length)
|
||||
if (e.key === 'ArrowRight') selectByIndex((activeIndex.value + 1) % props.tabs.length)
|
||||
if (e.key === 'Home') selectByIndex(0)
|
||||
if (e.key === 'End') selectByIndex(props.tabs.length - 1)
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="tabs">
|
||||
<div class="tablist" role="tablist" :aria-label="ariaLabel" @keydown="onKeydown">
|
||||
<button
|
||||
v-for="(t, i) in tabs"
|
||||
:key="t.key"
|
||||
class="tab"
|
||||
:class="{ active: t.key === internal }"
|
||||
role="tab"
|
||||
:id="`tab-${t.key}`"
|
||||
:aria-selected="t.key === internal"
|
||||
:tabindex="t.key === internal ? 0 : -1"
|
||||
@click="internal = t.key"
|
||||
>
|
||||
{{ t.label }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="tabpanel">
|
||||
<slot :active="internal"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.tabs { width: 100%; }
|
||||
.tablist { display: flex; justify-content: center; gap: 32px; border-bottom: 1px solid #e5e7eb; overflow-x: auto; }
|
||||
.tab { appearance: none; background: transparent; border: none; padding: 14px 4px; cursor: pointer; color: #6b7280; font-weight: 600; font-size: 14px; letter-spacing: .25px; white-space: nowrap; border-bottom: 3px solid transparent; margin-bottom: -1px; transition: color .18s, border-color .18s; }
|
||||
.tab.active { color: #111827; border-bottom-color: #FF6640; }
|
||||
.tab:hover { color: #111827; }
|
||||
.tabpanel { padding-top: 24px; }
|
||||
</style>
|
||||
7
src/main.js
Normal file
@ -0,0 +1,7 @@
|
||||
import './assets/main.css'
|
||||
|
||||
import { createApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
createApp(App).use(router).mount('#app')
|
||||
25
src/pages/Home.vue
Normal file
@ -0,0 +1,25 @@
|
||||
<script setup>
|
||||
import Hero from "@/components/main/Hero.vue";
|
||||
import ServicesMenu from "@/components/main/ServicesMenu.vue";
|
||||
import ServicesSection from "@/components/main/ServiceSection.vue";
|
||||
import WhyChoose from "@/components/main/WhyChoose.vue";
|
||||
import CallToAction from "@/components/main/CallToAction.vue";
|
||||
import ReasonsSection from "@/components/main/ReasonsSection.vue";
|
||||
import Testimonial from "@/components/main/Testimonial.vue";
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main>
|
||||
<Hero />
|
||||
<ServicesMenu id="solusi" />
|
||||
<!-- FIX: use section, not single card -->
|
||||
<ServicesSection />
|
||||
<WhyChoose />
|
||||
<CallToAction id="konsultasi" />
|
||||
<ReasonsSection />
|
||||
<Testimonial />
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
17
src/pages/Packages.vue
Normal file
@ -0,0 +1,17 @@
|
||||
<script setup>
|
||||
import PackagesSection from "../components/packages/PackagesSection.vue";
|
||||
import { useRoute } from 'vue-router';
|
||||
import { computed } from 'vue';
|
||||
|
||||
const route = useRoute();
|
||||
const category = computed(() => route.meta.category || null);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main>
|
||||
<PackagesSection id="paket" :initialCategory="category" />
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
||||
65
src/router/index.js
Normal file
@ -0,0 +1,65 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import Home from '@/pages/Home.vue'
|
||||
import Packages from '@/pages/Packages.vue'
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
name: 'Home',
|
||||
component: Home
|
||||
},
|
||||
{
|
||||
path: '/packages',
|
||||
name: 'Packages',
|
||||
component: Packages
|
||||
},
|
||||
{
|
||||
path: '/packages/pendirian',
|
||||
name: 'PackagesPendirian',
|
||||
component: Packages,
|
||||
meta: { category: 'pendirian' }
|
||||
},
|
||||
{
|
||||
path: '/packages/perubahan',
|
||||
name: 'PackagesPerubahan',
|
||||
component: Packages,
|
||||
meta: { category: 'perubahan' }
|
||||
},
|
||||
{
|
||||
path: '/packages/property',
|
||||
name: 'PackagesProperty',
|
||||
component: Packages,
|
||||
meta: { category: 'property' }
|
||||
},
|
||||
{
|
||||
path: '/packages/khusus',
|
||||
name: 'PackagesKhusus',
|
||||
component: Packages,
|
||||
meta: { category: 'khusus' }
|
||||
},
|
||||
{
|
||||
path: '/packages/umum',
|
||||
name: 'PackagesUmum',
|
||||
component: Packages,
|
||||
meta: { category: 'umum' }
|
||||
}
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
routes,
|
||||
scrollBehavior(to, from, savedPosition) {
|
||||
if (to.hash) {
|
||||
return {
|
||||
el: to.hash,
|
||||
behavior: 'smooth'
|
||||
}
|
||||
} else if (savedPosition) {
|
||||
return savedPosition
|
||||
} else {
|
||||
return { top: 0 }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default router
|
||||
18
vite.config.js
Normal file
@ -0,0 +1,18 @@
|
||||
import { fileURLToPath, URL } from 'node:url'
|
||||
|
||||
import { defineConfig } from 'vite'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import vueDevTools from 'vite-plugin-vue-devtools'
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
vueDevTools(),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': fileURLToPath(new URL('./src', import.meta.url))
|
||||
},
|
||||
},
|
||||
})
|
||||