Dashboard (/dashboard): - Welcome header with user name - Stats cards: upcoming sessions, completed, role (mentor/mentee) - Mentor registration CTA for non-mentors - Upcoming sessions list with status badges - Profile card with avatar and gradient header Settings (/dashboard/settings): - Account Details: displays user info and role - Privacy & Security: 2FA (coming soon), change password, active sessions - Preferences: email notifications, language, theme - FAQ & Support: expandable FAQ accordion with contact info Dashboard Layout: - Top nav with Dimentorin branding - Navigation links: Dashboard, Settings - Auth guard redirects to login if not authenticated - Logout button Closes #42, #43, #54 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
7268403ca4
commit
414e4d19b8
@@ -0,0 +1,66 @@
|
||||
import { Outlet, Link, useLocation, useNavigate } from 'react-router';
|
||||
import { useAuthStore } from '@imphnen-frontend-service/service';
|
||||
import { Icon } from '@iconify/react';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
const navItems = [
|
||||
{ path: '/dashboard', label: 'Dashboard', icon: 'mdi:view-dashboard' },
|
||||
{ path: '/dashboard/settings', label: 'Settings', icon: 'mdi:cog' },
|
||||
];
|
||||
|
||||
export default function DashboardLayout() {
|
||||
const { session, clearSession } = useAuthStore();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
if (!session?.token) navigate('/auth/login');
|
||||
}, [session, navigate]);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 dark:bg-gray-950">
|
||||
<nav className="bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-700">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="flex items-center justify-between h-16">
|
||||
<div className="flex items-center gap-6">
|
||||
<Link to="/" className="text-xl font-bold text-primary-600 dark:text-primary-400">
|
||||
Dimentorin
|
||||
</Link>
|
||||
<div className="hidden md:flex gap-1">
|
||||
{navItems.map((item) => (
|
||||
<Link
|
||||
key={item.path}
|
||||
to={item.path}
|
||||
className={`px-3 py-2 rounded-lg text-sm font-medium flex items-center gap-2 transition-colors ${
|
||||
location.pathname === item.path
|
||||
? 'bg-primary-50 dark:bg-primary-900/20 text-primary-600 dark:text-primary-400'
|
||||
: 'text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800'
|
||||
}`}
|
||||
>
|
||||
<Icon icon={item.icon} width="18" />
|
||||
{item.label}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="text-sm text-gray-600 dark:text-gray-400 hidden sm:block">
|
||||
{session?.user?.fullname || session?.user?.email}
|
||||
</span>
|
||||
<button
|
||||
onClick={() => { clearSession(); navigate('/auth/login'); }}
|
||||
className="text-sm text-red-500 hover:text-red-600 cursor-pointer flex items-center gap-1"
|
||||
>
|
||||
<Icon icon="mdi:logout" width="18" />
|
||||
<span className="hidden sm:inline">Logout</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
<Outlet />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
import { Link } from 'react-router';
|
||||
import {
|
||||
useAuthStore,
|
||||
useSessionQuery,
|
||||
useMySessions,
|
||||
useMentorMe,
|
||||
} from '@imphnen-frontend-service/service';
|
||||
import { Icon } from '@iconify/react';
|
||||
|
||||
export default function DashboardPage() {
|
||||
const { session } = useAuthStore();
|
||||
const { data: meData } = useSessionQuery(['mentor', 'sessions']);
|
||||
const { data: sessionsData } = useMySessions();
|
||||
const { data: mentorData } = useMentorMe();
|
||||
|
||||
const user = session?.user;
|
||||
const mentor = meData?.user?.mentor;
|
||||
const sessions = sessionsData?.data || [];
|
||||
|
||||
const upcomingSessions = sessions.filter(
|
||||
(s: { status: string }) => s.status === 'confirmed' || s.status === 'pending'
|
||||
);
|
||||
const completedSessions = sessions.filter(
|
||||
(s: { status: string }) => s.status === 'completed'
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold text-gray-900 dark:text-white">
|
||||
Welcome, {user?.fullname || user?.email?.split('@')[0] || 'User'}!
|
||||
</h1>
|
||||
<p className="text-gray-600 dark:text-gray-400 mt-1 font-sans">
|
||||
{mentor ? 'Manage your mentoring sessions and mentees' : 'Find mentors and book sessions'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Stats Cards */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
|
||||
<div className="bg-white dark:bg-gray-900 rounded-xl p-6 shadow-sm border border-gray-200 dark:border-gray-700">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<div className="w-10 h-10 rounded-lg bg-blue-100 dark:bg-blue-900/30 flex items-center justify-center">
|
||||
<Icon icon="mdi:calendar-clock" className="text-blue-600 dark:text-blue-400 text-xl" />
|
||||
</div>
|
||||
<span className="text-sm text-gray-600 dark:text-gray-400">Upcoming</span>
|
||||
</div>
|
||||
<p className="text-3xl font-bold text-gray-900 dark:text-white">{upcomingSessions.length}</p>
|
||||
</div>
|
||||
<div className="bg-white dark:bg-gray-900 rounded-xl p-6 shadow-sm border border-gray-200 dark:border-gray-700">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<div className="w-10 h-10 rounded-lg bg-green-100 dark:bg-green-900/30 flex items-center justify-center">
|
||||
<Icon icon="mdi:check-circle" className="text-green-600 dark:text-green-400 text-xl" />
|
||||
</div>
|
||||
<span className="text-sm text-gray-600 dark:text-gray-400">Completed</span>
|
||||
</div>
|
||||
<p className="text-3xl font-bold text-gray-900 dark:text-white">{completedSessions.length}</p>
|
||||
</div>
|
||||
<div className="bg-white dark:bg-gray-900 rounded-xl p-6 shadow-sm border border-gray-200 dark:border-gray-700">
|
||||
<div className="flex items-center gap-3 mb-2">
|
||||
<div className="w-10 h-10 rounded-lg bg-purple-100 dark:bg-purple-900/30 flex items-center justify-center">
|
||||
<Icon icon="mdi:account-star" className="text-purple-600 dark:text-purple-400 text-xl" />
|
||||
</div>
|
||||
<span className="text-sm text-gray-600 dark:text-gray-400">Role</span>
|
||||
</div>
|
||||
<p className="text-lg font-bold text-gray-900 dark:text-white">
|
||||
{mentor ? 'Mentor' : 'Mentee'}
|
||||
</p>
|
||||
{mentor?.status && (
|
||||
<span className={`inline-block mt-1 px-2 py-0.5 rounded text-xs font-medium ${
|
||||
mentor.status === 'verified'
|
||||
? 'bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-400'
|
||||
: 'bg-yellow-100 dark:bg-yellow-900/30 text-yellow-700 dark:text-yellow-400'
|
||||
}`}>
|
||||
{mentor.status}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mentor CTA */}
|
||||
{!mentor && (
|
||||
<div className="mb-8 bg-primary-50 dark:bg-primary-900/20 border border-primary-200 dark:border-primary-800 rounded-xl p-6">
|
||||
<div className="flex items-center justify-between flex-wrap gap-4">
|
||||
<div>
|
||||
<h3 className="font-bold text-primary-900 dark:text-primary-100 text-lg">Become a Mentor</h3>
|
||||
<p className="text-primary-700 dark:text-primary-300 text-sm mt-1">
|
||||
Share your knowledge and help others grow in their career.
|
||||
</p>
|
||||
</div>
|
||||
<Link
|
||||
to="/auth/register-mentor"
|
||||
className="px-6 py-2 bg-primary-600 hover:bg-primary-700 text-white font-medium rounded-lg transition-colors"
|
||||
>
|
||||
Register as Mentor
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Upcoming Sessions */}
|
||||
<div className="mb-8">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h2 className="text-xl font-bold text-gray-900 dark:text-white">
|
||||
{upcomingSessions.length > 0 ? 'Upcoming Sessions' : 'No Upcoming Sessions'}
|
||||
</h2>
|
||||
<Link
|
||||
to="/mentoring"
|
||||
className="text-sm text-primary-600 dark:text-primary-400 hover:text-primary-700 flex items-center gap-1"
|
||||
>
|
||||
Browse Mentors
|
||||
<Icon icon="mdi:chevron-right" width="18" />
|
||||
</Link>
|
||||
</div>
|
||||
{upcomingSessions.length > 0 ? (
|
||||
<div className="space-y-3">
|
||||
{upcomingSessions.slice(0, 5).map((s: any) => (
|
||||
<div key={s.id} className="bg-white dark:bg-gray-900 rounded-lg p-4 shadow-sm border border-gray-200 dark:border-gray-700 flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-10 h-10 rounded-full bg-primary-100 dark:bg-primary-900/30 flex items-center justify-center">
|
||||
<Icon icon="mdi:video" className="text-primary-600 dark:text-primary-400" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="font-medium text-gray-900 dark:text-white">{s.topic}</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
{new Date(s.scheduled_at).toLocaleDateString('en-US', {
|
||||
weekday: 'short', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit'
|
||||
})}
|
||||
{' '}·{' '}{s.duration_minutes} min
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<span className={`px-3 py-1 rounded-full text-xs font-medium ${
|
||||
s.status === 'confirmed'
|
||||
? 'bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-400'
|
||||
: 'bg-yellow-100 dark:bg-yellow-900/30 text-yellow-700 dark:text-yellow-400'
|
||||
}`}>
|
||||
{s.status}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<div className="bg-white dark:bg-gray-900 rounded-xl p-8 text-center shadow-sm border border-gray-200 dark:border-gray-700">
|
||||
<Icon icon="mdi:calendar-blank" className="text-5xl text-gray-300 dark:text-gray-600 mx-auto mb-3" />
|
||||
<p className="text-gray-500 dark:text-gray-400">
|
||||
No upcoming sessions. Browse mentors to book your first session!
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Profile Card */}
|
||||
<div className="bg-white dark:bg-gray-900 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden">
|
||||
<div className="bg-gradient-to-r from-primary-600 to-primary-500 h-20"></div>
|
||||
<div className="px-6 pb-6">
|
||||
<div className="flex items-end gap-4 -mt-10 mb-4">
|
||||
{user?.avatar ? (
|
||||
<img src={user.avatar} alt="" className="w-20 h-20 rounded-full border-4 border-white dark:border-gray-900 shadow-lg object-cover" />
|
||||
) : (
|
||||
<div className="w-20 h-20 rounded-full border-4 border-white dark:border-gray-900 shadow-lg bg-gray-200 dark:bg-gray-700 flex items-center justify-center">
|
||||
<span className="text-3xl text-gray-400">U</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="pb-1">
|
||||
<h3 className="font-bold text-gray-900 dark:text-white text-lg">{user?.fullname}</h3>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">{user?.email}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
useAuthStore,
|
||||
useSessionQuery,
|
||||
} from '@imphnen-frontend-service/service';
|
||||
import { Icon } from '@iconify/react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
type SettingsSection = 'account' | 'privacy' | 'preferences' | 'faq';
|
||||
|
||||
const faqItems = [
|
||||
{ q: 'How do I book a mentoring session?', a: 'Browse available mentors, select one, and click "Book Session" to schedule a meeting.' },
|
||||
{ q: 'How do I become a mentor?', a: 'Go to the registration page and fill in your professional details. Your application will be reviewed by our team.' },
|
||||
{ q: 'Can I cancel a session?', a: 'Yes, you can cancel a pending or confirmed session from your dashboard before the scheduled time.' },
|
||||
{ q: 'How do I update my profile?', a: 'Go to Settings > Account Details to update your personal information.' },
|
||||
{ q: 'Is my data secure?', a: 'Yes, we use encryption and secure protocols to protect your data. See our Privacy Policy for details.' },
|
||||
];
|
||||
|
||||
export default function SettingsPage() {
|
||||
const { session } = useAuthStore();
|
||||
const { data: meData } = useSessionQuery();
|
||||
const [activeSection, setActiveSection] = useState<SettingsSection>('account');
|
||||
const [expandedFaq, setExpandedFaq] = useState<number | null>(null);
|
||||
|
||||
const user = session?.user;
|
||||
|
||||
const sections = [
|
||||
{ id: 'account' as const, label: 'Account Details', icon: 'mdi:account-circle' },
|
||||
{ id: 'privacy' as const, label: 'Privacy & Security', icon: 'mdi:shield-lock' },
|
||||
{ id: 'preferences' as const, label: 'Preferences', icon: 'mdi:tune' },
|
||||
{ id: 'faq' as const, label: 'FAQ & Support', icon: 'mdi:help-circle' },
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold text-gray-900 dark:text-white mb-8">Settings</h1>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
|
||||
{/* Sidebar */}
|
||||
<div className="md:col-span-1">
|
||||
<div className="bg-white dark:bg-gray-900 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden">
|
||||
{sections.map((s) => (
|
||||
<button
|
||||
key={s.id}
|
||||
onClick={() => setActiveSection(s.id)}
|
||||
className={`w-full px-4 py-3 flex items-center gap-3 text-left text-sm font-medium transition-colors cursor-pointer ${
|
||||
activeSection === s.id
|
||||
? 'bg-primary-50 dark:bg-primary-900/20 text-primary-600 dark:text-primary-400 border-l-3 border-primary-600'
|
||||
: 'text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800'
|
||||
}`}
|
||||
>
|
||||
<Icon icon={s.icon} width="20" />
|
||||
{s.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="md:col-span-3">
|
||||
{activeSection === 'account' && (
|
||||
<div className="bg-white dark:bg-gray-900 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 p-6">
|
||||
<h2 className="text-xl font-bold text-gray-900 dark:text-white mb-6">Account Details</h2>
|
||||
<div className="space-y-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Full Name</label>
|
||||
<input type="text" defaultValue={user?.fullname || ''} disabled className="w-full px-4 py-2.5 border border-gray-300 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-800 text-gray-900 dark:text-white disabled:opacity-60" />
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Email</label>
|
||||
<input type="email" defaultValue={user?.email || ''} disabled className="w-full px-4 py-2.5 border border-gray-300 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-800 text-gray-900 dark:text-white disabled:opacity-60" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Role</label>
|
||||
<input type="text" defaultValue={user?.role?.name || 'User'} disabled className="w-full px-4 py-2.5 border border-gray-300 dark:border-gray-600 rounded-lg bg-gray-50 dark:bg-gray-800 text-gray-900 dark:text-white disabled:opacity-60" />
|
||||
</div>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400">
|
||||
To update your profile, use the profile page from the main menu.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeSection === 'privacy' && (
|
||||
<div className="bg-white dark:bg-gray-900 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 p-6">
|
||||
<h2 className="text-xl font-bold text-gray-900 dark:text-white mb-6">Privacy & Security</h2>
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
|
||||
<div>
|
||||
<p className="font-medium text-gray-900 dark:text-white">Two-Factor Authentication</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400 mt-1">Add an extra layer of security to your account</p>
|
||||
</div>
|
||||
<span className="px-3 py-1 bg-yellow-100 dark:bg-yellow-900/30 text-yellow-700 dark:text-yellow-400 rounded-full text-xs font-medium">Coming Soon</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
|
||||
<div>
|
||||
<p className="font-medium text-gray-900 dark:text-white">Change Password</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400 mt-1">Update your account password</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => toast.info('Use the forgot password flow to reset your password')}
|
||||
className="px-4 py-2 text-sm bg-primary-600 text-white rounded-lg hover:bg-primary-700 transition-colors cursor-pointer"
|
||||
>
|
||||
Change
|
||||
</button>
|
||||
</div>
|
||||
<div className="flex items-center justify-between p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
|
||||
<div>
|
||||
<p className="font-medium text-gray-900 dark:text-white">Active Sessions</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400 mt-1">You are currently logged in on this device</p>
|
||||
</div>
|
||||
<span className="flex items-center gap-1 text-green-600 dark:text-green-400 text-sm font-medium">
|
||||
<Icon icon="mdi:circle" width="8" />
|
||||
Active
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeSection === 'preferences' && (
|
||||
<div className="bg-white dark:bg-gray-900 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 p-6">
|
||||
<h2 className="text-xl font-bold text-gray-900 dark:text-white mb-6">Preferences</h2>
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
|
||||
<div>
|
||||
<p className="font-medium text-gray-900 dark:text-white">Email Notifications</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400 mt-1">Receive email updates about your sessions</p>
|
||||
</div>
|
||||
<span className="px-3 py-1 bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-400 rounded-full text-xs font-medium">Enabled</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
|
||||
<div>
|
||||
<p className="font-medium text-gray-900 dark:text-white">Language</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400 mt-1">Choose your preferred language</p>
|
||||
</div>
|
||||
<span className="text-sm text-gray-700 dark:text-gray-300 font-medium">English</span>
|
||||
</div>
|
||||
<div className="flex items-center justify-between p-4 bg-gray-50 dark:bg-gray-800 rounded-lg">
|
||||
<div>
|
||||
<p className="font-medium text-gray-900 dark:text-white">Theme</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400 mt-1">Toggle between light and dark mode</p>
|
||||
</div>
|
||||
<span className="text-sm text-gray-700 dark:text-gray-300 font-medium">System</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeSection === 'faq' && (
|
||||
<div className="bg-white dark:bg-gray-900 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 p-6">
|
||||
<h2 className="text-xl font-bold text-gray-900 dark:text-white mb-6">FAQ & Support</h2>
|
||||
<div className="space-y-3">
|
||||
{faqItems.map((item, i) => (
|
||||
<div key={i} className="border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden">
|
||||
<button
|
||||
onClick={() => setExpandedFaq(expandedFaq === i ? null : i)}
|
||||
className="w-full px-4 py-3 flex items-center justify-between text-left cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800"
|
||||
>
|
||||
<span className="font-medium text-gray-900 dark:text-white text-sm">{item.q}</span>
|
||||
<Icon icon={expandedFaq === i ? 'mdi:chevron-up' : 'mdi:chevron-down'} className="text-gray-400 shrink-0" />
|
||||
</button>
|
||||
{expandedFaq === i && (
|
||||
<div className="px-4 pb-3">
|
||||
<p className="text-sm text-gray-600 dark:text-gray-400">{item.a}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-6 p-4 bg-primary-50 dark:bg-primary-900/20 rounded-lg">
|
||||
<p className="text-sm text-primary-800 dark:text-primary-200">
|
||||
<strong>Need more help?</strong> Contact us at{' '}
|
||||
<a href="mailto:support@imphnen.dev" className="underline">support@imphnen.dev</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user