crud user
This commit is contained in:
parent
681059eccc
commit
a786de2801
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
class DashboardController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$user = Auth::user();
|
||||||
|
return inertia('admin/Dashboard', [
|
||||||
|
'user' => $user,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers\Admin;
|
||||||
|
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Inertia\Inertia;
|
use Inertia\Inertia;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
|
@ -17,7 +18,7 @@ public function index()
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
$user = User::paginate(10);
|
$user = User::paginate(10);
|
||||||
return Inertia::render('user/UserIndex', [
|
return Inertia::render('admin/user/UserIndex', [
|
||||||
'users' => $user,
|
'users' => $user,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
@ -28,7 +29,7 @@ public function index()
|
||||||
public function create()
|
public function create()
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
return Inertia::render('user/UserCreate');
|
return Inertia::render('admin/user/UserCreate');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -53,7 +54,7 @@ public function store(Request $request)
|
||||||
|
|
||||||
DB::commit();
|
DB::commit();
|
||||||
|
|
||||||
return redirect()->route('users.index')->with('success', 'User berhasil ditambahkan.');
|
return redirect()->route('admin.users.index')->with('success', 'User berhasil ditambahkan.');
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
DB::rollback();
|
DB::rollback();
|
||||||
return redirect()->back()->with('error', 'Gagal menambahkan user: ' . $e->getMessage());
|
return redirect()->back()->with('error', 'Gagal menambahkan user: ' . $e->getMessage());
|
||||||
|
|
@ -75,7 +76,7 @@ public function edit(string $id)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
$user = User::findOrFail($id);
|
$user = User::findOrFail($id);
|
||||||
return Inertia::render('user/UserEdit', [
|
return Inertia::render('admin/user/UserEdit', [
|
||||||
'user' => $user,
|
'user' => $user,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
@ -110,7 +111,7 @@ public function update(Request $request, string $id)
|
||||||
|
|
||||||
DB::commit();
|
DB::commit();
|
||||||
|
|
||||||
return redirect()->route('users.index')->with('success', 'User berhasil diupdate.');
|
return redirect()->route('admin.users.index')->with('success', 'User berhasil diupdate.');
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
DB::rollback();
|
DB::rollback();
|
||||||
return redirect()->back()->with('error', 'Gagal mengupdate user: ' . $e->getMessage());
|
return redirect()->back()->with('error', 'Gagal mengupdate user: ' . $e->getMessage());
|
||||||
|
|
@ -126,7 +127,7 @@ public function destroy(string $id)
|
||||||
try {
|
try {
|
||||||
$user = User::findOrFail($id);
|
$user = User::findOrFail($id);
|
||||||
$user->delete();
|
$user->delete();
|
||||||
return redirect()->route('users.index')->with('success', 'User berhasil dihapus.');
|
return redirect()->route('admin.users.index')->with('success', 'User berhasil dihapus.');
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return redirect()->back()->with('error', 'Gagal menghapus user: ' . $e->getMessage());
|
return redirect()->back()->with('error', 'Gagal menghapus user: ' . $e->getMessage());
|
||||||
}
|
}
|
||||||
|
|
@ -56,7 +56,7 @@ const activeItemStyles =
|
||||||
const mainNavItems: NavItem[] = [
|
const mainNavItems: NavItem[] = [
|
||||||
{
|
{
|
||||||
title: 'Dashboard',
|
title: 'Dashboard',
|
||||||
href: route('dashboard'),
|
href: route('admin.dashboard'),
|
||||||
icon: LayoutGrid,
|
icon: LayoutGrid,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
@ -146,7 +146,7 @@ const rightNavItems: NavItem[] = [
|
||||||
</Sheet>
|
</Sheet>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Link :href="route('dashboard')" class="flex items-center gap-x-2">
|
<Link :href="route('admin.dashboard')" class="flex items-center gap-x-2">
|
||||||
<AppLogo />
|
<AppLogo />
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,12 @@ import type { NavItem } from '@/types';
|
||||||
const mainNavItems: NavItem[] = [
|
const mainNavItems: NavItem[] = [
|
||||||
{
|
{
|
||||||
title: 'Dashboard',
|
title: 'Dashboard',
|
||||||
href: route('dashboard'),
|
href: route('admin.dashboard'),
|
||||||
icon: LayoutGrid,
|
icon: LayoutGrid,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Users',
|
title: 'Users',
|
||||||
href: route('users.index'),
|
href: route('admin.users.index'),
|
||||||
icon: Users,
|
icon: Users,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
@ -50,7 +50,7 @@ const footerNavItems: NavItem[] = [
|
||||||
<SidebarMenu>
|
<SidebarMenu>
|
||||||
<SidebarMenuItem>
|
<SidebarMenuItem>
|
||||||
<SidebarMenuButton size="lg" as-child>
|
<SidebarMenuButton size="lg" as-child>
|
||||||
<Link :href="route('dashboard')">
|
<Link :href="route('admin.dashboard')">
|
||||||
<AppLogo />
|
<AppLogo />
|
||||||
</Link>
|
</Link>
|
||||||
</SidebarMenuButton>
|
</SidebarMenuButton>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,150 @@
|
||||||
|
<template>
|
||||||
|
<section id="features" class="py-24 px-4">
|
||||||
|
<div class="max-w-6xl mx-auto">
|
||||||
|
<div class="text-center mb-16">
|
||||||
|
<h2 class="text-3xl sm:text-4xl font-bold text-white mb-4 font-display">Everything you need to ship</h2>
|
||||||
|
<p class="text-zinc-400 max-w-2xl mx-auto">
|
||||||
|
Built for modern teams. Powerful features that help you build, deploy, and scale faster than ever.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
|
<!-- Large card - System Status -->
|
||||||
|
<div
|
||||||
|
class="md:col-span-2 group relative p-6 rounded-2xl bg-zinc-900 border border-zinc-800 hover:border-zinc-600 hover:scale-[1.02] transition-all duration-300 overflow-hidden"
|
||||||
|
>
|
||||||
|
<div class="flex items-start justify-between mb-8">
|
||||||
|
<div>
|
||||||
|
<div class="p-2 rounded-lg bg-zinc-800 w-fit mb-4">
|
||||||
|
<Activity class="w-5 h-5 text-zinc-400" :stroke-width="1.5" />
|
||||||
|
</div>
|
||||||
|
<h3 class="text-xl font-semibold text-white mb-2">Real-time Monitoring</h3>
|
||||||
|
<p class="text-zinc-400 text-sm">
|
||||||
|
Track system health, performance metrics, and alerts in real-time across all your deployments.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<div
|
||||||
|
v-for="(active, i) in dots"
|
||||||
|
:key="i"
|
||||||
|
:class="[
|
||||||
|
'w-2 h-2 rounded-full',
|
||||||
|
active ? 'bg-emerald-500 animate-pulse' : 'bg-zinc-700',
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-4 gap-4">
|
||||||
|
<div v-for="metric in metrics" :key="metric" class="text-center">
|
||||||
|
<div class="text-2xl font-bold text-white mb-1">{{ Math.floor(Math.random() * 40 + 60) }}%</div>
|
||||||
|
<div class="text-xs text-zinc-500">{{ metric }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Command Palette -->
|
||||||
|
<div
|
||||||
|
class="group relative p-6 rounded-2xl bg-zinc-900 border border-zinc-800 hover:border-zinc-600 hover:scale-[1.02] transition-all duration-300"
|
||||||
|
>
|
||||||
|
<div class="p-2 rounded-lg bg-zinc-800 w-fit mb-4">
|
||||||
|
<Command class="w-5 h-5 text-zinc-400" :stroke-width="1.5" />
|
||||||
|
</div>
|
||||||
|
<h3 class="text-lg font-semibold text-white mb-2">Command Palette</h3>
|
||||||
|
<p class="text-zinc-400 text-sm mb-6">Navigate anywhere instantly with powerful keyboard shortcuts.</p>
|
||||||
|
<div class="flex items-center gap-1">
|
||||||
|
<kbd class="px-2 py-1 text-xs bg-zinc-800 border border-zinc-700 rounded text-zinc-300 font-mono">
|
||||||
|
⌘
|
||||||
|
</kbd>
|
||||||
|
<kbd class="px-2 py-1 text-xs bg-zinc-800 border border-zinc-700 rounded text-zinc-300 font-mono">
|
||||||
|
K
|
||||||
|
</kbd>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Analytics -->
|
||||||
|
<div
|
||||||
|
class="group relative p-6 rounded-2xl bg-zinc-900 border border-zinc-800 hover:border-zinc-600 hover:scale-[1.02] transition-all duration-300"
|
||||||
|
>
|
||||||
|
<div class="p-2 rounded-lg bg-zinc-800 w-fit mb-4">
|
||||||
|
<BarChart3 class="w-5 h-5 text-zinc-400" :stroke-width="1.5" />
|
||||||
|
</div>
|
||||||
|
<h3 class="text-lg font-semibold text-white mb-2">Analytics</h3>
|
||||||
|
<p class="text-zinc-400 text-sm mb-4">Deep insights into your application performance.</p>
|
||||||
|
<svg viewBox="0 0 100 70" class="w-full h-24">
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="chartGradient" x1="0" y1="0" x2="0" y2="1">
|
||||||
|
<stop offset="0%" stop-color="rgb(255,255,255)" stop-opacity="0.2" />
|
||||||
|
<stop offset="100%" stop-color="rgb(255,255,255)" stop-opacity="0" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<path
|
||||||
|
d="M 0 60 L 20 45 L 40 55 L 60 30 L 80 40 L 100 15 L 100 70 L 0 70 Z"
|
||||||
|
fill="url(#chartGradient)"
|
||||||
|
class="opacity-50"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M 0 60 L 20 45 L 40 55 L 60 30 L 80 40 L 100 15"
|
||||||
|
fill="none"
|
||||||
|
stroke="white"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Performance -->
|
||||||
|
<div
|
||||||
|
class="group relative p-6 rounded-2xl bg-zinc-900 border border-zinc-800 hover:border-zinc-600 hover:scale-[1.02] transition-all duration-300"
|
||||||
|
>
|
||||||
|
<div class="p-2 rounded-lg bg-zinc-800 w-fit mb-4">
|
||||||
|
<Zap class="w-5 h-5 text-zinc-400" :stroke-width="1.5" />
|
||||||
|
</div>
|
||||||
|
<h3 class="text-lg font-semibold text-white mb-2">Blazing Fast</h3>
|
||||||
|
<p class="text-zinc-400 text-sm mb-4">
|
||||||
|
Edge-optimized infrastructure for sub-50ms response times globally.
|
||||||
|
</p>
|
||||||
|
<div class="flex items-center gap-2 text-emerald-500 text-sm">
|
||||||
|
<span class="font-mono">~32ms</span>
|
||||||
|
<span class="text-zinc-500">avg response</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Security -->
|
||||||
|
<div
|
||||||
|
class="group relative p-6 rounded-2xl bg-zinc-900 border border-zinc-800 hover:border-zinc-600 hover:scale-[1.02] transition-all duration-300"
|
||||||
|
>
|
||||||
|
<div class="p-2 rounded-lg bg-zinc-800 w-fit mb-4">
|
||||||
|
<Shield class="w-5 h-5 text-zinc-400" :stroke-width="1.5" />
|
||||||
|
</div>
|
||||||
|
<h3 class="text-lg font-semibold text-white mb-2">Enterprise Security</h3>
|
||||||
|
<p class="text-zinc-400 text-sm mb-4">SOC2 compliant with end-to-end encryption and SSO support.</p>
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<span class="px-2 py-1 text-xs bg-zinc-800 rounded text-zinc-400">SOC2</span>
|
||||||
|
<span class="px-2 py-1 text-xs bg-zinc-800 rounded text-zinc-400">GDPR</span>
|
||||||
|
<span class="px-2 py-1 text-xs bg-zinc-800 rounded text-zinc-400">HIPAA</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
||||||
|
import { Activity, Command, BarChart3, Zap, Shield } from 'lucide-vue-next'
|
||||||
|
|
||||||
|
const dots = ref([true, true, true, false, true])
|
||||||
|
const metrics = ['CPU', 'Memory', 'Network', 'Storage']
|
||||||
|
|
||||||
|
let interval: NodeJS.Timeout
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
interval = setInterval(() => {
|
||||||
|
dots.value = dots.value.map(() => Math.random() > 0.2)
|
||||||
|
}, 2000)
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
clearInterval(interval)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
<template>
|
||||||
|
<section class="py-24 px-4">
|
||||||
|
<div class="max-w-4xl mx-auto text-center">
|
||||||
|
<h2 class="text-4xl sm:text-5xl lg:text-6xl font-bold text-white mb-6 tracking-tight font-display">
|
||||||
|
Ready to ship faster?
|
||||||
|
</h2>
|
||||||
|
<p class="text-lg sm:text-xl text-zinc-400 mb-10 max-w-2xl mx-auto">
|
||||||
|
Join thousands of teams already building with Apex. Start free, no credit card required.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="flex flex-col sm:flex-row items-center justify-center gap-4">
|
||||||
|
<button
|
||||||
|
class="shimmer-btn bg-white text-zinc-950 hover:bg-zinc-200 rounded-full px-8 py-3 text-base font-medium shadow-lg shadow-white/20 flex items-center gap-2"
|
||||||
|
>
|
||||||
|
Get Started for Free
|
||||||
|
<ArrowRight :size="20" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="rounded-full px-8 py-3 text-base font-medium border border-zinc-800 text-zinc-300 hover:bg-zinc-900 hover:text-white hover:border-zinc-700 bg-transparent"
|
||||||
|
>
|
||||||
|
Talk to Sales
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p class="mt-8 text-sm text-zinc-500">Free forever for individuals. Team plans start at $29/month.</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ArrowRight } from 'lucide-vue-next'
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
<template>
|
||||||
|
<footer class="border-t border-zinc-800 bg-zinc-950">
|
||||||
|
<div class="max-w-6xl mx-auto px-4 py-16">
|
||||||
|
<div class="grid grid-cols-2 md:grid-cols-5 gap-8">
|
||||||
|
<!-- Brand -->
|
||||||
|
<div class="col-span-2 md:col-span-1">
|
||||||
|
<a href="#" class="flex items-center gap-2 mb-4">
|
||||||
|
<div class="w-8 h-8 rounded-lg bg-white flex items-center justify-center">
|
||||||
|
<span class="text-zinc-950 font-bold text-sm">A</span>
|
||||||
|
</div>
|
||||||
|
<span class="font-semibold text-white">Apex</span>
|
||||||
|
</a>
|
||||||
|
<p class="text-sm text-zinc-500 mb-4">The modern platform for teams who ship fast.</p>
|
||||||
|
<div class="inline-flex items-center gap-2 px-3 py-1.5 rounded-full bg-zinc-900 border border-zinc-800">
|
||||||
|
<span class="w-2 h-2 rounded-full bg-emerald-500 pulse-glow" />
|
||||||
|
<span class="text-xs text-zinc-400">All Systems Operational</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Links -->
|
||||||
|
<div v-for="(links, title) in footerLinks" :key="title">
|
||||||
|
<h4 class="text-sm font-semibold text-white mb-4">{{ title }}</h4>
|
||||||
|
<ul class="space-y-3">
|
||||||
|
<li v-for="link in links" :key="link">
|
||||||
|
<a href="#" class="text-sm text-zinc-500 hover:text-white transition-colors">
|
||||||
|
{{ link }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bottom -->
|
||||||
|
<div
|
||||||
|
class="mt-16 pt-8 border-t border-zinc-800 flex flex-col sm:flex-row items-center justify-between gap-4"
|
||||||
|
>
|
||||||
|
<p class="text-sm text-zinc-500">© {{ new Date().getFullYear() }} Apex, Inc. All rights reserved.</p>
|
||||||
|
<div class="flex items-center gap-6">
|
||||||
|
<a href="#" class="text-sm text-zinc-500 hover:text-white transition-colors">Twitter</a>
|
||||||
|
<a href="#" class="text-sm text-zinc-500 hover:text-white transition-colors">GitHub</a>
|
||||||
|
<a href="#" class="text-sm text-zinc-500 hover:text-white transition-colors">Discord</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const footerLinks: Record<string, string[]> = {
|
||||||
|
Product: ['Features', 'Pricing', 'Changelog', 'Roadmap', 'API'],
|
||||||
|
Resources: ['Documentation', 'Guides', 'Blog', 'Community', 'Templates'],
|
||||||
|
Company: ['About', 'Careers', 'Press', 'Partners', 'Contact'],
|
||||||
|
Legal: ['Privacy', 'Terms', 'Security', 'Cookies', 'Licenses'],
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,78 @@
|
||||||
|
<template>
|
||||||
|
<section
|
||||||
|
class="relative min-h-screen flex flex-col items-center justify-center px-4 pt-24 pb-16 overflow-hidden"
|
||||||
|
>
|
||||||
|
<!-- Background gradient -->
|
||||||
|
<div
|
||||||
|
class="absolute inset-0 bg-gradient-to-b from-zinc-950 via-zinc-950 to-zinc-900 pointer-events-none"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Subtle radial glow -->
|
||||||
|
<div
|
||||||
|
class="absolute top-1/4 left-1/2 -translate-x-1/2 w-[800px] h-[600px] bg-zinc-800/20 rounded-full blur-3xl pointer-events-none"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="relative z-10 max-w-5xl mx-auto text-center">
|
||||||
|
<!-- Badge -->
|
||||||
|
<div class="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-zinc-900 border border-zinc-800 mb-8">
|
||||||
|
<span class="w-2 h-2 rounded-full bg-emerald-500 pulse-glow" />
|
||||||
|
<span class="text-sm text-zinc-400">Now in Public Beta</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Headline -->
|
||||||
|
<h1 class="text-5xl sm:text-6xl lg:text-7xl font-bold tracking-tight text-white mb-6 font-display">
|
||||||
|
<span class="block">Ship faster.</span>
|
||||||
|
<span class="block text-zinc-500">Scale smarter.</span>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<!-- Subheadline -->
|
||||||
|
<p class="text-lg sm:text-xl text-zinc-400 max-w-2xl mx-auto mb-10 leading-relaxed">
|
||||||
|
The modern platform for teams who ship fast. Built for scale, designed for speed. Everything you need to build,
|
||||||
|
deploy, and grow.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- CTAs -->
|
||||||
|
<div class="flex flex-col sm:flex-row items-center justify-center gap-4 mb-16">
|
||||||
|
<button
|
||||||
|
class="shimmer-btn bg-white text-zinc-950 hover:bg-zinc-200 rounded-full px-8 py-3 text-base font-medium shadow-lg shadow-white/10 flex items-center gap-2"
|
||||||
|
>
|
||||||
|
Start Building
|
||||||
|
<ArrowRight :size="16" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="rounded-full px-8 py-3 text-base font-medium border border-zinc-800 text-zinc-300 hover:bg-zinc-900 hover:text-white hover:border-zinc-700 bg-transparent"
|
||||||
|
>
|
||||||
|
View Demo
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Social Proof -->
|
||||||
|
<div class="flex flex-col items-center gap-4">
|
||||||
|
<div class="flex items-center -space-x-3">
|
||||||
|
<img
|
||||||
|
v-for="(avatar, index) in avatars"
|
||||||
|
:key="index"
|
||||||
|
:src="avatar"
|
||||||
|
:alt="`Avatar ${index + 1}`"
|
||||||
|
class="w-10 h-10 rounded-full border-2 border-zinc-950 object-cover"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p class="text-sm text-zinc-500">
|
||||||
|
Trusted by <span class="text-zinc-300 font-medium">2,000+</span> teams worldwide
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ArrowRight } from 'lucide-vue-next'
|
||||||
|
|
||||||
|
const avatars = [
|
||||||
|
'/professional-headshot-1.png',
|
||||||
|
'/professional-headshot-2.png',
|
||||||
|
'/professional-headshot-3.png',
|
||||||
|
'/professional-headshot-4.png',
|
||||||
|
'/professional-headshot-5.png',
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
<template>
|
||||||
|
<section class="py-16 overflow-hidden">
|
||||||
|
<div class="text-center mb-10">
|
||||||
|
<p class="text-sm text-zinc-500 uppercase tracking-wider font-medium">Trusted by industry leaders</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="relative">
|
||||||
|
<!-- Fade masks -->
|
||||||
|
<div
|
||||||
|
class="absolute left-0 top-0 bottom-0 w-32 bg-gradient-to-r from-zinc-950 to-transparent z-10 pointer-events-none"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
class="absolute right-0 top-0 bottom-0 w-32 bg-gradient-to-l from-zinc-950 to-transparent z-10 pointer-events-none"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Marquee container -->
|
||||||
|
<div class="flex animate-marquee">
|
||||||
|
<div v-for="logo in [...logos, ...logos]" :key="`${logo.name}-marquee`" class="flex items-center justify-center min-w-[160px] h-16 mx-8 grayscale opacity-50 hover:grayscale-0 hover:opacity-100 transition-all duration-300">
|
||||||
|
<div class="flex items-center gap-2 text-zinc-400">
|
||||||
|
<div class="w-8 h-8 rounded-lg bg-zinc-800 flex items-center justify-center">
|
||||||
|
<span class="text-xs font-bold">{{ logo.name[0] }}</span>
|
||||||
|
</div>
|
||||||
|
<span class="font-medium">{{ logo.name }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const logos = [
|
||||||
|
{ name: 'Vercel', width: 100 },
|
||||||
|
{ name: 'Stripe', width: 80 },
|
||||||
|
{ name: 'Linear', width: 90 },
|
||||||
|
{ name: 'Notion', width: 100 },
|
||||||
|
{ name: 'Figma', width: 70 },
|
||||||
|
{ name: 'Slack', width: 90 },
|
||||||
|
{ name: 'Discord', width: 100 },
|
||||||
|
{ name: 'GitHub', width: 90 },
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,99 @@
|
||||||
|
<template>
|
||||||
|
<header class="fixed top-4 left-1/2 -translate-x-1/2 z-50 w-[calc(100%-2rem)] max-w-3xl">
|
||||||
|
<nav
|
||||||
|
class="relative flex items-center justify-between px-4 py-3 rounded-full bg-zinc-900/40 backdrop-blur-md border border-zinc-800"
|
||||||
|
>
|
||||||
|
<!-- Logo -->
|
||||||
|
<a href="#" class="flex items-center gap-2">
|
||||||
|
<div class="w-8 h-8 rounded-lg bg-white flex items-center justify-center">
|
||||||
|
<span class="text-zinc-950 font-bold text-sm">A</span>
|
||||||
|
</div>
|
||||||
|
<span class="font-semibold text-white hidden sm:block">Apex</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- Desktop Nav Items -->
|
||||||
|
<div class="hidden md:flex items-center gap-1 relative">
|
||||||
|
<a
|
||||||
|
v-for="(item, index) in navItems"
|
||||||
|
:key="item.label"
|
||||||
|
:href="item.href"
|
||||||
|
class="relative px-4 py-2 text-sm text-zinc-400 hover:text-white transition-colors"
|
||||||
|
@mouseenter="hoveredIndex = index"
|
||||||
|
@mouseleave="hoveredIndex = null"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="hoveredIndex === index"
|
||||||
|
class="absolute inset-0 bg-zinc-800 rounded-full"
|
||||||
|
/>
|
||||||
|
<span class="relative z-10">{{ item.label }}</span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- CTA Buttons -->
|
||||||
|
<div class="hidden md:flex items-center gap-3">
|
||||||
|
<button
|
||||||
|
class="px-4 py-2 text-sm text-zinc-400 hover:text-white hover:bg-zinc-800 rounded-lg transition-colors"
|
||||||
|
>
|
||||||
|
Sign In
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="shimmer-btn bg-white text-zinc-950 hover:bg-zinc-200 rounded-full px-4 py-2 text-sm font-medium"
|
||||||
|
>
|
||||||
|
Get Started
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Mobile Menu Button -->
|
||||||
|
<button
|
||||||
|
class="md:hidden p-2 text-zinc-400 hover:text-white"
|
||||||
|
@click="mobileMenuOpen = !mobileMenuOpen"
|
||||||
|
:aria-label="mobileMenuOpen ? 'Close menu' : 'Open menu'"
|
||||||
|
>
|
||||||
|
<Menu v-if="!mobileMenuOpen" :size="20" />
|
||||||
|
<X v-else :size="20" />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Mobile Menu -->
|
||||||
|
<div
|
||||||
|
v-if="mobileMenuOpen"
|
||||||
|
class="absolute top-full left-0 right-0 mt-2 p-4 rounded-2xl bg-zinc-900/95 backdrop-blur-md border border-zinc-800"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<a
|
||||||
|
v-for="item in navItems"
|
||||||
|
:key="item.label"
|
||||||
|
:href="item.href"
|
||||||
|
class="px-4 py-3 text-sm text-zinc-400 hover:text-white hover:bg-zinc-800 rounded-lg transition-colors"
|
||||||
|
@click="mobileMenuOpen = false"
|
||||||
|
>
|
||||||
|
{{ item.label }}
|
||||||
|
</a>
|
||||||
|
<hr class="border-zinc-800 my-2" />
|
||||||
|
<button class="px-4 py-3 text-sm text-zinc-400 hover:text-white hover:bg-zinc-800 rounded-lg transition-colors text-left">
|
||||||
|
Sign In
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="shimmer-btn bg-white text-zinc-950 hover:bg-zinc-200 rounded-full px-4 py-2 text-sm font-medium w-full"
|
||||||
|
>
|
||||||
|
Get Started
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { Menu, X } from 'lucide-vue-next'
|
||||||
|
|
||||||
|
const navItems = [
|
||||||
|
{ label: 'Features', href: '#features' },
|
||||||
|
{ label: 'Pricing', href: '#pricing' },
|
||||||
|
{ label: 'Docs', href: '#docs' },
|
||||||
|
{ label: 'Blog', href: '#blog' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const hoveredIndex = ref<number | null>(null)
|
||||||
|
const mobileMenuOpen = ref(false)
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,148 @@
|
||||||
|
<template>
|
||||||
|
<section id="pricing" class="py-24 px-4">
|
||||||
|
<div class="max-w-6xl mx-auto">
|
||||||
|
<div class="text-center mb-12">
|
||||||
|
<h2 class="text-3xl sm:text-4xl font-bold text-white mb-4 font-display">Simple, transparent pricing</h2>
|
||||||
|
<p class="text-zinc-400 max-w-2xl mx-auto mb-8">Start free, scale as you grow. No hidden fees, no surprises.</p>
|
||||||
|
|
||||||
|
<!-- Billing Toggle -->
|
||||||
|
<div class="inline-flex items-center p-1 rounded-full bg-zinc-900 border border-zinc-800">
|
||||||
|
<button
|
||||||
|
@click="billingCycle = 'monthly'"
|
||||||
|
:class="[
|
||||||
|
'relative px-4 py-2 text-sm font-medium rounded-full transition-colors',
|
||||||
|
billingCycle === 'monthly' ? 'text-white' : 'text-zinc-400',
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="billingCycle === 'monthly'"
|
||||||
|
class="absolute inset-0 bg-zinc-800 rounded-full"
|
||||||
|
/>
|
||||||
|
<span class="relative z-10">Monthly</span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
@click="billingCycle = 'yearly'"
|
||||||
|
:class="[
|
||||||
|
'relative px-4 py-2 text-sm font-medium rounded-full transition-colors',
|
||||||
|
billingCycle === 'yearly' ? 'text-white' : 'text-zinc-400',
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="billingCycle === 'yearly'"
|
||||||
|
class="absolute inset-0 bg-zinc-800 rounded-full"
|
||||||
|
/>
|
||||||
|
<span class="relative z-10">Yearly</span>
|
||||||
|
<span class="relative z-10 ml-2 px-2 py-0.5 text-xs bg-emerald-500/20 text-emerald-400 rounded-full">
|
||||||
|
-20%
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||||
|
<div
|
||||||
|
v-for="(plan, index) in plans"
|
||||||
|
:key="plan.name"
|
||||||
|
:class="[
|
||||||
|
'relative p-6 rounded-2xl border transition-all duration-300 hover:scale-[1.02]',
|
||||||
|
plan.highlighted
|
||||||
|
? 'bg-zinc-900 border-zinc-700'
|
||||||
|
: 'bg-zinc-900/50 border-zinc-800 hover:border-zinc-600',
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="plan.highlighted"
|
||||||
|
class="absolute -top-3 left-1/2 -translate-x-1/2 px-3 py-1 bg-white text-zinc-950 text-xs font-medium rounded-full"
|
||||||
|
>
|
||||||
|
Most Popular
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-6">
|
||||||
|
<h3 class="text-xl font-semibold text-white mb-2">{{ plan.name }}</h3>
|
||||||
|
<p class="text-zinc-400 text-sm">{{ plan.description }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-6">
|
||||||
|
<div class="flex items-baseline gap-1">
|
||||||
|
<span class="text-4xl font-bold text-white">${{ plan.price[billingCycle] }}</span>
|
||||||
|
<span v-if="plan.price.monthly > 0" class="text-zinc-400 text-sm">/month</span>
|
||||||
|
</div>
|
||||||
|
<p v-if="billingCycle === 'yearly' && plan.price.yearly > 0" class="text-xs text-zinc-500 mt-1">
|
||||||
|
Billed annually (${{ plan.price.yearly * 12 }}/year)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul class="space-y-3 mb-8">
|
||||||
|
<li v-for="feature in plan.features" :key="feature" class="flex items-center gap-3 text-sm text-zinc-300">
|
||||||
|
<Check class="w-4 h-4 text-emerald-500 shrink-0" :stroke-width="1.5" />
|
||||||
|
{{ feature }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<button
|
||||||
|
:class="[
|
||||||
|
'w-full rounded-full py-2 font-medium transition-colors',
|
||||||
|
plan.highlighted
|
||||||
|
? 'shimmer-btn bg-white text-zinc-950 hover:bg-zinc-200'
|
||||||
|
: 'bg-zinc-800 text-white hover:bg-zinc-700 border border-zinc-700',
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
{{ plan.cta }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { Check } from 'lucide-vue-next'
|
||||||
|
|
||||||
|
type BillingCycle = 'monthly' | 'yearly'
|
||||||
|
|
||||||
|
const billingCycle = ref<BillingCycle>('monthly')
|
||||||
|
|
||||||
|
const plans = [
|
||||||
|
{
|
||||||
|
name: 'Starter',
|
||||||
|
description: 'Perfect for side projects and small teams',
|
||||||
|
price: { monthly: 0, yearly: 0 },
|
||||||
|
features: ['3 team members', '10 projects', 'Basic analytics', 'Community support', '1GB storage'],
|
||||||
|
cta: 'Get Started',
|
||||||
|
highlighted: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Pro',
|
||||||
|
description: 'For growing teams that need more power',
|
||||||
|
price: { monthly: 29, yearly: 24 },
|
||||||
|
features: [
|
||||||
|
'Unlimited team members',
|
||||||
|
'Unlimited projects',
|
||||||
|
'Advanced analytics',
|
||||||
|
'Priority support',
|
||||||
|
'100GB storage',
|
||||||
|
'Custom domains',
|
||||||
|
'API access',
|
||||||
|
],
|
||||||
|
cta: 'Start Free Trial',
|
||||||
|
highlighted: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Enterprise',
|
||||||
|
description: 'For organizations with advanced needs',
|
||||||
|
price: { monthly: 99, yearly: 79 },
|
||||||
|
features: [
|
||||||
|
'Everything in Pro',
|
||||||
|
'SSO & SAML',
|
||||||
|
'Dedicated support',
|
||||||
|
'SLA guarantee',
|
||||||
|
'Unlimited storage',
|
||||||
|
'Custom integrations',
|
||||||
|
'Audit logs',
|
||||||
|
],
|
||||||
|
cta: 'Contact Sales',
|
||||||
|
highlighted: false,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
</script>
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import { Head } from '@inertiajs/vue3';
|
|
||||||
import { route } from 'ziggy-js';
|
|
||||||
import PlaceholderPattern from '@/components/PlaceholderPattern.vue';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
layout: {
|
|
||||||
breadcrumbs: [
|
|
||||||
{
|
|
||||||
title: 'Dashboard',
|
|
||||||
href: route('dashboard'),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<Head title="Dashboard" />
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="flex h-full flex-1 flex-col gap-4 overflow-x-auto rounded-xl p-4"
|
|
||||||
>
|
|
||||||
<div class="grid auto-rows-min gap-4 md:grid-cols-3">
|
|
||||||
<div
|
|
||||||
class="relative aspect-video overflow-hidden rounded-xl border border-sidebar-border/70 dark:border-sidebar-border"
|
|
||||||
>
|
|
||||||
<PlaceholderPattern />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="relative aspect-video overflow-hidden rounded-xl border border-sidebar-border/70 dark:border-sidebar-border"
|
|
||||||
>
|
|
||||||
<PlaceholderPattern />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="relative aspect-video overflow-hidden rounded-xl border border-sidebar-border/70 dark:border-sidebar-border"
|
|
||||||
>
|
|
||||||
<PlaceholderPattern />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="relative min-h-[100vh] flex-1 rounded-xl border border-sidebar-border/70 md:min-h-min dark:border-sidebar-border"
|
|
||||||
>
|
|
||||||
<PlaceholderPattern />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
@ -26,7 +26,7 @@ withDefaults(
|
||||||
<nav class="flex items-center justify-end gap-4">
|
<nav class="flex items-center justify-end gap-4">
|
||||||
<Link
|
<Link
|
||||||
v-if="$page.props.auth.user"
|
v-if="$page.props.auth.user"
|
||||||
:href="route('dashboard')"
|
:href="route('admin.dashboard')"
|
||||||
class="inline-block rounded-sm border border-[#19140035] px-5 py-1.5 text-sm leading-normal text-[#1b1b18] hover:border-[#1915014a] dark:border-[#3E3E3A] dark:text-[#EDEDEC] dark:hover:border-[#62605b]"
|
class="inline-block rounded-sm border border-[#19140035] px-5 py-1.5 text-sm leading-normal text-[#1b1b18] hover:border-[#1915014a] dark:border-[#3E3E3A] dark:text-[#EDEDEC] dark:hover:border-[#62605b]"
|
||||||
>
|
>
|
||||||
Dashboard
|
Dashboard
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Head } from '@inertiajs/vue3';
|
||||||
|
import { route } from 'ziggy-js';
|
||||||
|
import PlaceholderPattern from '@/components/PlaceholderPattern.vue';
|
||||||
|
import { Card } from '@/components/ui/card';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
layout: {
|
||||||
|
breadcrumbs: [
|
||||||
|
{
|
||||||
|
title: 'Dashboard',
|
||||||
|
href: route('admin.dashboard'),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { user } = defineProps<{ user: User }>();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Head title="Dashboard" />
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="flex h-full flex-1 flex-col gap-4 overflow-x-auto rounded-xl p-4"
|
||||||
|
>
|
||||||
|
<!-- card greetings -->
|
||||||
|
<Card class="flex-1 border-border bg-card">
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h3 class="text-base font-medium text-foreground">
|
||||||
|
Selamat Datang {{ user.name }}
|
||||||
|
</h3>
|
||||||
|
<p class="text-sm text-muted-foreground">
|
||||||
|
ini adalah dashboard admin
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -107,7 +107,7 @@ const form = useForm({
|
||||||
});
|
});
|
||||||
|
|
||||||
const submit = () => {
|
const submit = () => {
|
||||||
form.post(route('users.store'), {
|
form.post(route('admin.users.store'), {
|
||||||
preserveScroll: true,
|
preserveScroll: true,
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
form.reset();
|
form.reset();
|
||||||
|
|
@ -41,7 +41,7 @@ const props = defineProps<{
|
||||||
const deleteUser = () => {
|
const deleteUser = () => {
|
||||||
isDeleting.value = true;
|
isDeleting.value = true;
|
||||||
|
|
||||||
router.delete(route('users.destroy', props.userId), {
|
router.delete(route('admin.users.destroy', props.userId), {
|
||||||
preserveScroll: true,
|
preserveScroll: true,
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
open.value = false;
|
open.value = false;
|
||||||
|
|
@ -126,7 +126,7 @@ watch(open, (value) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const submit = () => {
|
const submit = () => {
|
||||||
form.put(route('users.update', props.user.id), {
|
form.put(route('admin.users.update', props.user.id), {
|
||||||
preserveScroll: true,
|
preserveScroll: true,
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
open.value = false;
|
open.value = false;
|
||||||
|
|
@ -11,7 +11,7 @@ defineOptions({
|
||||||
breadcrumbs: [
|
breadcrumbs: [
|
||||||
{
|
{
|
||||||
title: 'User',
|
title: 'User',
|
||||||
href: route('users.index'),
|
href: route('admin.users.index'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
<template>
|
||||||
|
<div class="bg-background text-foreground min-h-screen">
|
||||||
|
<Navbar />
|
||||||
|
<Hero />
|
||||||
|
<LogoMarquee />
|
||||||
|
<BentoGrid />
|
||||||
|
<Pricing />
|
||||||
|
<FinalCta />
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import Navbar from '@/components/public/Navbar.vue'
|
||||||
|
import Hero from '@/components/public/Hero.vue'
|
||||||
|
import LogoMarquee from '@/components/public/LogoMarquee.vue'
|
||||||
|
import BentoGrid from '@/components/public/BentoGrid.vue'
|
||||||
|
import Pricing from '@/components/public/Pricing.vue'
|
||||||
|
import FinalCta from '@/components/public/FinalCta.vue'
|
||||||
|
import Footer from '@/components/public/Footer.vue'
|
||||||
|
</script>
|
||||||
|
|
@ -2,17 +2,22 @@
|
||||||
|
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use Laravel\Fortify\Features;
|
use Laravel\Fortify\Features;
|
||||||
use App\Http\Controllers\UserController;
|
use App\Http\Controllers\Admin\UserController;
|
||||||
|
use App\Http\Controllers\Admin\DashboardController;
|
||||||
|
|
||||||
Route::inertia('/', 'Welcome', [
|
Route::redirect('dashboard', 'admin/dashboard')->name('dashboard');
|
||||||
'canRegister' => Features::enabled(Features::registration()),
|
|
||||||
])->name('home');
|
|
||||||
|
|
||||||
Route::middleware(['auth', 'verified'])->group(function () {
|
Route::get('/', function () {
|
||||||
Route::inertia('dashboard', 'Dashboard')->name('dashboard');
|
return inertia('public/Home', [
|
||||||
|
'canRegister' => Features::enabled(Features::registration()),
|
||||||
|
]);
|
||||||
|
})->name('home');
|
||||||
|
|
||||||
|
Route::prefix('admin')->name('admin.')->middleware(['auth', 'verified'])->group(function () {
|
||||||
|
Route::get('dashboard', [DashboardController::class, 'index'])->name('dashboard');
|
||||||
Route::resource('users', UserController::class)->names('users');
|
Route::resource('users', UserController::class)->names('users');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
require __DIR__.'/settings.php';
|
require __DIR__ . '/settings.php';
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
|
||||||
test('guests are redirected to the login page', function () {
|
test('guests are redirected to the login page', function () {
|
||||||
$response = $this->get(route('dashboard'));
|
$response = $this->get(route('admin.dashboard'));
|
||||||
$response->assertRedirect(route('login'));
|
$response->assertRedirect(route('login'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -11,6 +11,6 @@
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
|
|
||||||
$response = $this->get(route('dashboard'));
|
$response = $this->get(route('admin.dashboard'));
|
||||||
$response->assertOk();
|
$response->assertOk();
|
||||||
});
|
});
|
||||||
Loading…
Reference in New Issue