improvement Quiz Interactive new
This commit is contained in:
parent
bcb9aabc35
commit
2d96a4d65e
|
|
@ -1,7 +1,7 @@
|
||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
images: {
|
images: {
|
||||||
domains: ['localhost', 'api.lms-bgn.id'],
|
domains: ['localhost', 'api.lms-bgn.id', 'upload.wikimedia.org'],
|
||||||
formats: ['image/webp', 'image/avif'],
|
formats: ['image/webp', 'image/avif'],
|
||||||
},
|
},
|
||||||
// PWA Configuration
|
// PWA Configuration
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { PayrollManagement } from '@/features/payroll-reward-system/components';
|
||||||
|
import DashboardLayout from '@/layouts/DashboardLayout';
|
||||||
|
|
||||||
|
export default function AdminPayrollPage() {
|
||||||
|
return (
|
||||||
|
<DashboardLayout>
|
||||||
|
<div className="mb-6">
|
||||||
|
<h1 className="text-2xl font-bold text-gray-900">Admin Payroll</h1>
|
||||||
|
<p className="text-gray-600">Kelola perhitungan dan pembayaran payroll peserta.</p>
|
||||||
|
</div>
|
||||||
|
<PayrollManagement userRole="admin" />
|
||||||
|
</DashboardLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
import Image from 'next/image';
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { Eye, EyeOff, Mail, Lock, AlertCircle, CheckCircle } from 'lucide-react';
|
import { Eye, EyeOff, Mail, Lock, AlertCircle, CheckCircle } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
|
@ -95,8 +96,15 @@ export default function LoginPage() {
|
||||||
<div className="w-full max-w-md">
|
<div className="w-full max-w-md">
|
||||||
{/* Logo and Title */}
|
{/* Logo and Title */}
|
||||||
<div className="text-center mb-8">
|
<div className="text-center mb-8">
|
||||||
<div className="w-16 h-16 bg-blue-600 rounded-2xl flex items-center justify-center mx-auto mb-4">
|
<div className="mx-auto mb-4">
|
||||||
<span className="text-2xl font-bold text-white">LMS</span>
|
<Image
|
||||||
|
src="https://upload.wikimedia.org/wikipedia/id/thumb/2/29/Logo_Badan_Gizi_Nasional.svg/480px-Logo_Badan_Gizi_Nasional.svg.png"
|
||||||
|
alt="Logo Badan Gizi Nasional"
|
||||||
|
width={64}
|
||||||
|
height={64}
|
||||||
|
className="rounded-lg"
|
||||||
|
priority
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-3xl font-bold text-gray-900 mb-2">Selamat Datang</h1>
|
<h1 className="text-3xl font-bold text-gray-900 mb-2">Selamat Datang</h1>
|
||||||
<p className="text-gray-600">Masuk ke akun Learning Management System Anda</p>
|
<p className="text-gray-600">Masuk ke akun Learning Management System Anda</p>
|
||||||
|
|
|
||||||
|
|
@ -227,44 +227,7 @@ export default function Dashboard() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Development Demo Section */}
|
{/* Development Demo Section moved to Sidebar under Add-ons */}
|
||||||
<div className="bg-gradient-to-r from-indigo-50 to-purple-50 rounded-lg shadow-sm p-6 border border-indigo-200">
|
|
||||||
<div className="flex items-start">
|
|
||||||
<div className="text-3xl mr-4">🚀</div>
|
|
||||||
<div className="flex-1">
|
|
||||||
<h2 className="text-lg font-semibold text-gray-900 mb-2">
|
|
||||||
Development Demo - Modular Architecture
|
|
||||||
</h2>
|
|
||||||
<p className="text-gray-600 mb-4">
|
|
||||||
Lihat implementasi EPIC 17: Participant Payroll Reward System menggunakan arsitektur modular baru.
|
|
||||||
Demo ini menampilkan sistem reward, wallet management, dan payroll calculation.
|
|
||||||
</p>
|
|
||||||
<div className="flex flex-wrap gap-2 mb-4">
|
|
||||||
<span className="bg-indigo-100 text-indigo-800 px-2 py-1 rounded text-xs font-medium">
|
|
||||||
Dependency Injection
|
|
||||||
</span>
|
|
||||||
<span className="bg-purple-100 text-purple-800 px-2 py-1 rounded text-xs font-medium">
|
|
||||||
Event Bus System
|
|
||||||
</span>
|
|
||||||
<span className="bg-blue-100 text-blue-800 px-2 py-1 rounded text-xs font-medium">
|
|
||||||
Plugin Architecture
|
|
||||||
</span>
|
|
||||||
<span className="bg-green-100 text-green-800 px-2 py-1 rounded text-xs font-medium">
|
|
||||||
Service Layer
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<Link
|
|
||||||
href="/payroll-demo"
|
|
||||||
className="inline-flex items-center px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition-colors font-medium"
|
|
||||||
>
|
|
||||||
🎯 Lihat Demo Payroll System
|
|
||||||
<svg className="ml-2 h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
|
||||||
</svg>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Recent Activities Section */}
|
{/* Recent Activities Section */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import {
|
||||||
import { PayrollRewardSystemModule } from '../../features/payroll-reward-system';
|
import { PayrollRewardSystemModule } from '../../features/payroll-reward-system';
|
||||||
import { setupGlobalErrorHandling } from '../../core/errors';
|
import { setupGlobalErrorHandling } from '../../core/errors';
|
||||||
import { container as globalContainer } from '../../core/di/DIContainer';
|
import { container as globalContainer } from '../../core/di/DIContainer';
|
||||||
|
import DashboardLayout from '@/layouts/DashboardLayout';
|
||||||
|
|
||||||
export default function PayrollDemoPage() {
|
export default function PayrollDemoPage() {
|
||||||
const [moduleLoaded, setModuleLoaded] = useState(false);
|
const [moduleLoaded, setModuleLoaded] = useState(false);
|
||||||
|
|
@ -87,46 +88,51 @@ export default function PayrollDemoPage() {
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
|
<DashboardLayout>
|
||||||
<div className="text-center">
|
<div className="min-h-[calc(100vh-4rem)] bg-gray-50 flex items-center justify-center">
|
||||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div>
|
<div className="text-center">
|
||||||
<p className="text-gray-600">Loading Payroll Reward System...</p>
|
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4"></div>
|
||||||
|
<p className="text-gray-600">Loading Payroll Reward System...</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</DashboardLayout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
|
<DashboardLayout>
|
||||||
<div className="text-center max-w-md">
|
<div className="min-h-[calc(100vh-4rem)] bg-gray-50 flex items-center justify-center">
|
||||||
<div className="text-6xl mb-4">❌</div>
|
<div className="text-center max-w-md">
|
||||||
<h1 className="text-2xl font-bold text-gray-900 mb-2">Module Load Error</h1>
|
<div className="text-6xl mb-4">❌</div>
|
||||||
<p className="text-gray-600 mb-4">{error}</p>
|
<h1 className="text-2xl font-bold text-gray-900 mb-2">Module Load Error</h1>
|
||||||
<button
|
<p className="text-gray-600 mb-4">{error}</p>
|
||||||
onClick={initializeModule}
|
<button
|
||||||
className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700"
|
onClick={initializeModule}
|
||||||
>
|
className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700"
|
||||||
Retry
|
>
|
||||||
</button>
|
Retry
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</DashboardLayout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PayrollErrorBoundary>
|
<DashboardLayout>
|
||||||
<div className="min-h-screen bg-gray-50">
|
<PayrollErrorBoundary>
|
||||||
|
<div className="min-h-[calc(100vh-4rem)] bg-gray-50">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="bg-white shadow-sm border-b">
|
<div className="bg-white shadow-sm border-b">
|
||||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
<div className="flex justify-between items-center py-4">
|
<div className="flex justify-between items-center py-4">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-2xl font-bold text-gray-900">
|
<h1 className="text-2xl font-bold text-gray-900">
|
||||||
🎯 Payroll Reward System Demo
|
🎯 Payroll Reward System
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-gray-600">
|
<p className="text-gray-600">
|
||||||
Modular Architecture Implementation - EPIC 17
|
Modular Payroll Reward System
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -175,38 +181,6 @@ export default function PayrollDemoPage() {
|
||||||
|
|
||||||
{/* Content */}
|
{/* Content */}
|
||||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||||
{/* Module Info Card */}
|
|
||||||
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4 mb-6">
|
|
||||||
<div className="flex items-start">
|
|
||||||
<div className="text-2xl mr-3">ℹ️</div>
|
|
||||||
<div>
|
|
||||||
<h3 className="font-semibold text-blue-900 mb-1">
|
|
||||||
Modular Architecture Demo
|
|
||||||
</h3>
|
|
||||||
<p className="text-blue-800 text-sm mb-2">
|
|
||||||
This demo showcases the implementation of EPIC 17 using our new modular architecture.
|
|
||||||
The system includes reward calculation, wallet management, and payroll processing.
|
|
||||||
</p>
|
|
||||||
<div className="flex flex-wrap gap-2 text-xs">
|
|
||||||
<span className="bg-blue-100 text-blue-800 px-2 py-1 rounded">
|
|
||||||
✅ Core DI Container
|
|
||||||
</span>
|
|
||||||
<span className="bg-blue-100 text-blue-800 px-2 py-1 rounded">
|
|
||||||
✅ Event Bus System
|
|
||||||
</span>
|
|
||||||
<span className="bg-blue-100 text-blue-800 px-2 py-1 rounded">
|
|
||||||
✅ Feature Module Plugin
|
|
||||||
</span>
|
|
||||||
<span className="bg-blue-100 text-blue-800 px-2 py-1 rounded">
|
|
||||||
✅ Service Layer
|
|
||||||
</span>
|
|
||||||
<span className="bg-blue-100 text-blue-800 px-2 py-1 rounded">
|
|
||||||
✅ React Components
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Component Content */}
|
{/* Component Content */}
|
||||||
{moduleLoaded && (
|
{moduleLoaded && (
|
||||||
|
|
@ -234,67 +208,9 @@ export default function PayrollDemoPage() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Architecture Info */}
|
|
||||||
<div className="mt-8 bg-white rounded-lg shadow-md p-6">
|
|
||||||
<h3 className="text-lg font-semibold mb-4">🏗️ Architecture Overview</h3>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
||||||
<div className="border rounded-lg p-4">
|
|
||||||
<h4 className="font-semibold text-gray-900 mb-2">🔧 Core Layer</h4>
|
|
||||||
<ul className="text-sm text-gray-600 space-y-1">
|
|
||||||
<li>• Dependency Injection Container</li>
|
|
||||||
<li>• Event Bus System</li>
|
|
||||||
<li>• Base Interfaces (IService, IRepository)</li>
|
|
||||||
<li>• Plugin Architecture</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="border rounded-lg p-4">
|
|
||||||
<h4 className="font-semibold text-gray-900 mb-2">🎯 Feature Module</h4>
|
|
||||||
<ul className="text-sm text-gray-600 space-y-1">
|
|
||||||
<li>• RewardService</li>
|
|
||||||
<li>• WalletService</li>
|
|
||||||
<li>• PayrollCalculator</li>
|
|
||||||
<li>• Type Definitions</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="border rounded-lg p-4">
|
|
||||||
<h4 className="font-semibold text-gray-900 mb-2">🎨 UI Components</h4>
|
|
||||||
<ul className="text-sm text-gray-600 space-y-1">
|
|
||||||
<li>• RewardDashboard</li>
|
|
||||||
<li>• WalletBalance</li>
|
|
||||||
<li>• PayrollManagement</li>
|
|
||||||
<li>• Responsive Design</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mt-6 p-4 bg-gray-50 rounded-lg">
|
|
||||||
<h4 className="font-semibold text-gray-900 mb-2">📋 Implementation Status</h4>
|
|
||||||
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 text-sm">
|
|
||||||
<div className="flex items-center">
|
|
||||||
<span className="text-green-500 mr-2">✅</span>
|
|
||||||
<span>Core Architecture</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center">
|
|
||||||
<span className="text-green-500 mr-2">✅</span>
|
|
||||||
<span>Service Layer</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center">
|
|
||||||
<span className="text-green-500 mr-2">✅</span>
|
|
||||||
<span>UI Components</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center">
|
|
||||||
<span className="text-yellow-500 mr-2">⏳</span>
|
|
||||||
<span>Data Persistence</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</PayrollErrorBoundary>
|
</PayrollErrorBoundary>
|
||||||
|
</DashboardLayout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -142,12 +142,12 @@ export const RewardDashboard: React.FC<RewardDashboardProps> = ({
|
||||||
if (error) {
|
if (error) {
|
||||||
return (
|
return (
|
||||||
<div className={`bg-white rounded-lg shadow-md p-6 ${className}`}>
|
<div className={`bg-white rounded-lg shadow-md p-6 ${className}`}>
|
||||||
<div className="text-center text-red-600">
|
<div className="text-center text-error-600">
|
||||||
<p className="text-lg font-semibold mb-2">Error</p>
|
<p className="text-lg font-semibold mb-2">Error</p>
|
||||||
<p>{error}</p>
|
<p>{error}</p>
|
||||||
<button
|
<button
|
||||||
onClick={loadRewardData}
|
onClick={loadRewardData}
|
||||||
className="mt-4 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
|
className="mt-4 px-4 py-2 bg-primary-600 text-white rounded hover:bg-primary-700"
|
||||||
>
|
>
|
||||||
Coba Lagi
|
Coba Lagi
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -168,7 +168,7 @@ export const RewardDashboard: React.FC<RewardDashboardProps> = ({
|
||||||
onClick={() => setSelectedPeriod(period)}
|
onClick={() => setSelectedPeriod(period)}
|
||||||
className={`px-3 py-1 rounded text-sm font-medium ${
|
className={`px-3 py-1 rounded text-sm font-medium ${
|
||||||
selectedPeriod === period
|
selectedPeriod === period
|
||||||
? 'bg-blue-600 text-white'
|
? 'bg-primary-600 text-white'
|
||||||
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
: 'bg-gray-100 text-gray-700 hover:bg-gray-200'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
|
|
@ -181,30 +181,30 @@ export const RewardDashboard: React.FC<RewardDashboardProps> = ({
|
||||||
{/* Statistics Cards */}
|
{/* Statistics Cards */}
|
||||||
{stats && (
|
{stats && (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
|
||||||
<div className="bg-gradient-to-r from-blue-500 to-blue-600 rounded-lg p-4 text-white">
|
<div className="rounded-lg p-4" style={{ backgroundColor: '#071e49', color: '#ffffff' }}>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-blue-100 text-sm">Total Earned</p>
|
<p className="text-primary-100 text-sm">Total Earned</p>
|
||||||
<p className="text-2xl font-bold">{formatCurrency(stats.totalEarned)}</p>
|
<p className="text-2xl font-bold">{formatCurrency(stats.totalEarned)}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-3xl opacity-80">💰</div>
|
<div className="text-3xl opacity-80">💰</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="bg-gradient-to-r from-green-500 to-green-600 rounded-lg p-4 text-white">
|
<div className="rounded-lg p-4" style={{ backgroundColor: '#071e49', color: '#ffffff' }}>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-green-100 text-sm">Periode Ini</p>
|
<p className="text-success-100 text-sm">Periode Ini</p>
|
||||||
<p className="text-2xl font-bold">{formatCurrency(stats.thisMonthEarnings)}</p>
|
<p className="text-2xl font-bold">{formatCurrency(stats.thisMonthEarnings)}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-3xl opacity-80">📈</div>
|
<div className="text-3xl opacity-80">📈</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="bg-gradient-to-r from-yellow-500 to-yellow-600 rounded-lg p-4 text-white">
|
<div className="rounded-lg p-4" style={{ backgroundColor: '#071e49', color: '#ffffff' }}>
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-yellow-100 text-sm">Pending</p>
|
<p className="text-warning-100 text-sm">Pending</p>
|
||||||
<p className="text-2xl font-bold">{formatCurrency(stats.pendingRewards)}</p>
|
<p className="text-2xl font-bold">{formatCurrency(stats.pendingRewards)}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-3xl opacity-80">⏳</div>
|
<div className="text-3xl opacity-80">⏳</div>
|
||||||
|
|
@ -292,7 +292,7 @@ export const RewardDashboard: React.FC<RewardDashboardProps> = ({
|
||||||
{/* View All Link */}
|
{/* View All Link */}
|
||||||
{rewards.length > 10 && (
|
{rewards.length > 10 && (
|
||||||
<div className="mt-4 text-center">
|
<div className="mt-4 text-center">
|
||||||
<button className="text-blue-600 hover:text-blue-800 font-medium">
|
<button className="text-primary-600 hover:text-primary-800 font-medium">
|
||||||
Lihat Semua Reward ({rewards.length})
|
Lihat Semua Reward ({rewards.length})
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { usePathname } from 'next/navigation';
|
import { usePathname } from 'next/navigation';
|
||||||
|
import Image from 'next/image';
|
||||||
|
import { useAuth } from '@/contexts/AuthContext';
|
||||||
import {
|
import {
|
||||||
HomeIcon,
|
HomeIcon,
|
||||||
BookOpenIcon,
|
BookOpenIcon,
|
||||||
|
|
@ -14,7 +16,9 @@ import {
|
||||||
ChartBarIcon,
|
ChartBarIcon,
|
||||||
CogIcon,
|
CogIcon,
|
||||||
XMarkIcon,
|
XMarkIcon,
|
||||||
Bars3Icon
|
Bars3Icon,
|
||||||
|
BanknotesIcon,
|
||||||
|
ShieldCheckIcon
|
||||||
} from '@heroicons/react/24/outline';
|
} from '@heroicons/react/24/outline';
|
||||||
import { cn } from '@/utils/cn';
|
import { cn } from '@/utils/cn';
|
||||||
|
|
||||||
|
|
@ -37,8 +41,14 @@ const navigation = [
|
||||||
{ name: 'Pengaturan', href: '/settings', icon: CogIcon },
|
{ name: 'Pengaturan', href: '/settings', icon: CogIcon },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const addons = [
|
||||||
|
{ name: 'Payroll Reward System', href: '/payroll-demo', icon: BanknotesIcon },
|
||||||
|
{ name: 'Admin Payroll', href: '/admin/payroll', icon: ShieldCheckIcon },
|
||||||
|
];
|
||||||
|
|
||||||
export function Sidebar({ isOpen, isCollapsed, onClose, onToggleCollapse }: SidebarProps) {
|
export function Sidebar({ isOpen, isCollapsed, onClose, onToggleCollapse }: SidebarProps) {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
|
const { user } = useAuth();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
@ -53,9 +63,16 @@ export function Sidebar({ isOpen, isCollapsed, onClose, onToggleCollapse }: Side
|
||||||
<div className="flex h-16 items-center justify-between px-4">
|
<div className="flex h-16 items-center justify-between px-4">
|
||||||
{!isCollapsed && (
|
{!isCollapsed && (
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<div className="flex h-8 w-8 items-center justify-center rounded bg-gold-500">
|
<div className="h-8 w-8">
|
||||||
<AcademicCapIcon className="h-5 w-5 text-white" />
|
<Image
|
||||||
</div>
|
src="https://upload.wikimedia.org/wikipedia/id/thumb/2/29/Logo_Badan_Gizi_Nasional.svg/480px-Logo_Badan_Gizi_Nasional.svg.png"
|
||||||
|
alt="Logo Badan Gizi Nasional"
|
||||||
|
width={32}
|
||||||
|
height={32}
|
||||||
|
className="rounded"
|
||||||
|
priority
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className="text-white">
|
<div className="text-white">
|
||||||
<div className="text-sm font-bold">LMS BGN</div>
|
<div className="text-sm font-bold">LMS BGN</div>
|
||||||
<div className="text-xs text-secondary-300">Badan Gizi Nasional</div>
|
<div className="text-xs text-secondary-300">Badan Gizi Nasional</div>
|
||||||
|
|
@ -108,6 +125,39 @@ export function Sidebar({ isOpen, isCollapsed, onClose, onToggleCollapse }: Side
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
{/* Add-ons section */}
|
||||||
|
{!isCollapsed && (
|
||||||
|
<div className="mt-4 mb-1 px-2 text-xs font-semibold text-primary-300 uppercase tracking-wider">
|
||||||
|
Add-ons
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{addons
|
||||||
|
.filter(item => !item.href.startsWith('/admin') || user?.role === 'admin')
|
||||||
|
.map((item) => {
|
||||||
|
const isActive = pathname === item.href;
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
key={item.name}
|
||||||
|
href={item.href}
|
||||||
|
className={cn(
|
||||||
|
'group flex items-center rounded-lg px-2 py-2 text-sm font-medium transition-colors',
|
||||||
|
isActive
|
||||||
|
? 'bg-indigo-600 text-white shadow-md'
|
||||||
|
: 'text-sidebar-300 hover:bg-sidebar-800 hover:text-white',
|
||||||
|
isCollapsed ? 'justify-center' : 'justify-start'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<item.icon className={cn(
|
||||||
|
'flex-shrink-0',
|
||||||
|
isCollapsed ? 'h-6 w-6' : 'mr-3 h-5 w-5'
|
||||||
|
)} />
|
||||||
|
{!isCollapsed && (
|
||||||
|
<span className="truncate">{item.name}</span>
|
||||||
|
)}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{/* User info */}
|
{/* User info */}
|
||||||
|
|
@ -135,8 +185,15 @@ export function Sidebar({ isOpen, isCollapsed, onClose, onToggleCollapse }: Side
|
||||||
{/* Mobile header */}
|
{/* Mobile header */}
|
||||||
<div className="flex h-16 items-center justify-between px-4">
|
<div className="flex h-16 items-center justify-between px-4">
|
||||||
<div className="flex items-center space-x-2">
|
<div className="flex items-center space-x-2">
|
||||||
<div className="flex h-8 w-8 items-center justify-center rounded bg-gradient-to-r from-secondary-400 to-gold-500">
|
<div className="h-8 w-8">
|
||||||
<AcademicCapIcon className="h-5 w-5 text-white" />
|
<Image
|
||||||
|
src="https://upload.wikimedia.org/wikipedia/id/thumb/2/29/Logo_Badan_Gizi_Nasional.svg/480px-Logo_Badan_Gizi_Nasional.svg.png"
|
||||||
|
alt="Logo Badan Gizi Nasional"
|
||||||
|
width={32}
|
||||||
|
height={32}
|
||||||
|
className="rounded"
|
||||||
|
priority
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-white">
|
<div className="text-white">
|
||||||
<div className="text-sm font-bold">LMS BGN</div>
|
<div className="text-sm font-bold">LMS BGN</div>
|
||||||
|
|
@ -184,6 +241,34 @@ export function Sidebar({ isOpen, isCollapsed, onClose, onToggleCollapse }: Side
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
{/* Add-ons section */}
|
||||||
|
<div className="mt-4 mb-1 px-2 text-xs font-semibold text-primary-300 uppercase tracking-wider">
|
||||||
|
Add-ons
|
||||||
|
</div>
|
||||||
|
{addons
|
||||||
|
.filter(item => !item.href.startsWith('/admin') || user?.role === 'admin')
|
||||||
|
.map((item) => {
|
||||||
|
const isActive = pathname === item.href;
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
key={item.name}
|
||||||
|
href={item.href}
|
||||||
|
onClick={onClose}
|
||||||
|
className={cn(
|
||||||
|
'group flex items-center justify-between rounded-lg px-2 py-2 text-sm font-medium transition-colors',
|
||||||
|
isActive
|
||||||
|
? 'bg-indigo-600 text-white shadow-md'
|
||||||
|
: 'text-sidebar-300 hover:bg-sidebar-800 hover:text-white'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center">
|
||||||
|
<item.icon className="mr-3 h-5 w-5 flex-shrink-0" />
|
||||||
|
<span className="truncate">{item.name}</span>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
{/* Mobile user info */}
|
{/* Mobile user info */}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue