- {/* Action Queue */}
-
-
-
- {/* Lead Pipeline */}
-
-
-
- {pipeline.winRate30d.toFixed(1)}% Conversion
-
-
-
-
-
- {/* Listing Health */}
-
-
-
-
80 ? 'green' : healthScore > 50 ? 'yellow' : 'red'} />
-
-
Improve visibility:
- {action_queue.missingFields.slice(0, 3).map((field: string) => (
-
-
- Add {field.replace('_json', '').replace('_', ' ')}
-
- ))}
-
- Enhance Profile →
-
-
-
-
-
-
-
- {/* Recent Messages */}
-
-
-
- {recentMessages.length > 0 ? recentMessages.map((msg: any) => (
-
-
- {msg.sender_user?.firstName?.[0] || 'U'}
-
-
-
- {msg.sender_user?.firstName} {msg.sender_user?.lastName}
- {moment(msg.createdAt).fromNow()}
-
-
"{msg.body}"
-
-
-
-
-
- )) : (
-
No recent messages yet
- )}
-
-
-
-
-
-
- {/* Performance & Billing */}
-
-
-
-
-
-
Views
-
{performance.views30d}
-
-
- 7d: {performance.views7d}
-
-
-
-
Interactions
-
{performance.calls30d + performance.website30d}
-
-
- 7d: {performance.calls7d + performance.website7d}
-
-
-
-
-
- Conversion Rate
- {performance.conversionRate.toFixed(1)}%
-
-
-
-
-
-
-
-
-
-
Tier Plan
-
{business.plan?.name || 'Professional'}
-
-
-
-
-
-
-
Renewal Date
-
{moment(business.renewal_date).format('MMMM Do, YYYY')}
-
-
-
-
-
-
+
+
+
+
+ {props.title}
+
+
+ {props.number}
+
+
+ {props.label}
+
+
-
- );
-};
+
+ )
+}
const Dashboard = () => {
- const dispatch = useAppDispatch();
- const iconsColor = useAppSelector((state) => state.style.iconsColor);
+ const { currentUser } = useAppSelector((state) => state.auth)
+ const [dashboardData, setDashboardData] = useState
(null)
+ const [isFetching, setIsFetching] = useState(false)
+
+ const isBusinessOwner = currentUser?.role === 'Verified Business Owner'
- const { currentUser } = useAppSelector((state) => state.auth);
- const isBusinessOwner = currentUser?.app_role?.name === 'Verified Business Owner';
-
- const [loading, setLoading] = useState(true);
- const [metrics, setMetrics] = useState(null);
-
- const [counts, setCounts] = useState({});
-
- async function loadAdminData() {
- const entities = ['users','roles','permissions','categories','locations','businesses'];
- const requests = entities.map(entity => axios.get(`/${entity}/count`));
- const results = await Promise.allSettled(requests);
- const newCounts: any = {};
- results.forEach((result, i) => {
- if (result.status === 'fulfilled') {
- newCounts[entities[i]] = result.value.data.count;
- }
- });
- setCounts(newCounts);
+ useEffect(() => {
+ const fetchDashboard = async () => {
+ setIsFetching(true)
+ try {
+ const response = await axios.get('/dashboard')
+ setDashboardData(response.data)
+ } catch (error) {
+ console.error('Error fetching dashboard data:', error)
+ } finally {
+ setIsFetching(false)
+ }
}
- async function loadBusinessMetrics() {
- try {
- const response = await axios.get('/dashboard/business-metrics');
- setMetrics(response.data);
- } catch (err) {
- console.error('Failed to load metrics', err);
- } finally {
- setLoading(false);
- }
- }
+ fetchDashboard()
+ }, [])
- useEffect(() => {
- if (!currentUser) return;
- if (isBusinessOwner) {
- loadBusinessMetrics();
- } else {
- loadAdminData().then(() => setLoading(false));
- }
- }, [currentUser, isBusinessOwner]);
+ const stats = [
+ {
+ title: 'Total Views',
+ icon: mdiChartTimelineVariant,
+ number: dashboardData?.totalViews || 0,
+ label: 'Overall visibility',
+ color: 'success' as ColorKey,
+ },
+ {
+ title: 'Active Leads',
+ icon: mdiCartOutline,
+ number: dashboardData?.activeLeads || 0,
+ label: 'Potential clients',
+ color: 'info' as ColorKey,
+ },
+ {
+ title: 'Conversion Rate',
+ icon: mdiShieldCheck,
+ number: dashboardData?.conversionRate ? `${dashboardData.conversionRate}%` : '0%',
+ label: 'Leads to jobs',
+ color: 'warning' as ColorKey,
+ },
+ ]
- if (loading) {
- return (
-
-
-
-
-
-
Curating your experience...
-
-
- );
- }
+ const adminStats = [
+ {
+ title: 'Active Users',
+ icon: mdiAccountMultiple,
+ number: dashboardData?.totalUsers || 0,
+ label: 'Verified members',
+ color: 'success' as ColorKey,
+ },
+ {
+ title: 'Total Businesses',
+ icon: mdiStore,
+ number: dashboardData?.totalBusinesses || 0,
+ label: 'Listed companies',
+ color: 'info' as ColorKey,
+ },
+ {
+ title: 'Revenue Flow',
+ icon: mdiCurrencyUsd,
+ number: dashboardData?.totalRevenue ? `$${dashboardData.totalRevenue.toLocaleString()}` : '$0',
+ label: 'Network throughput',
+ color: 'warning' as ColorKey,
+ },
+ ]
return (
<>
- {getPageTitle('Beauty Studio Dashboard')}
+ {getPageTitle('Dashboard')}
- {isBusinessOwner && (
-
-
-
-
- )}
+ {''}
- {isBusinessOwner ? (
-
- ) : (
-
- {Object.keys(counts).map(entity => (
-
-
-
-
-
{entity.replace('_', ' ')}
-
{counts[entity]}
-
-
-
-
-
-
-
- ))}
+
+ {(isBusinessOwner ? stats : adminStats).map((stat, index) => (
+
+ ))}
+
+
+
+ {/* Quick Actions */}
+
+
+
+ {isBusinessOwner ? (
+ <>
+
+
+ Add Business
+
+
+
+ Manage Leads
+
+ >
+ ) : (
+ <>
+
+
+ Review Users
+
+
+
+ Verifications
+
+ >
+ )}
+
+
+ Open Disputes
+
+
+
+ Inbox
+
+
+
+
+ {/* Recent Activity / Status */}
+
+
+
+
+
+
+
+
+
+
Verification Status
+
Identity & Business verified
+
+
+
Active
+
+
+
+
+
+
+
+
+
Account Standing
+
Perfect track record
+
+
+
Good
+
+
+
+
+
+ {/* Informational Widget */}
+
+
+
+
+
+ {isBusinessOwner
+ ? 'Welcome to your professional control center. From here you can manage your listings, respond to new leads, and track your business growth across our verified network.'
+ : 'The network is operating at optimal capacity. All verification systems are online and AI matching is currently processing requests with 98% efficiency.'}
+
+
+
+ View Documentation
+
+
+ Contact Support
+
+
- )}
+
>
)
}
-Dashboard.getLayout = function getLayout(page: ReactElement) {
+Dashboard.getLayout = function getLayout(page: React.ReactElement) {
return
{page}
}
-export default Dashboard;
\ No newline at end of file
+export default Dashboard
\ No newline at end of file
diff --git a/frontend/src/pages/forgot.tsx b/frontend/src/pages/forgot.tsx
index 41f239f..8752f9f 100644
--- a/frontend/src/pages/forgot.tsx
+++ b/frontend/src/pages/forgot.tsx
@@ -96,7 +96,7 @@ export default function Forgot() {
- Fix It Local™
+ Fix-It-Local™
Forgot Password?
@@ -138,7 +138,7 @@ export default function Forgot() {
- © 2026 Fix It Local™. All rights reserved.
+ © 2026 Fix-It-Local™. All rights reserved.
Privacy Policy
diff --git a/frontend/src/pages/index.tsx b/frontend/src/pages/index.tsx
index 2c4f6a4..ca8593b 100644
--- a/frontend/src/pages/index.tsx
+++ b/frontend/src/pages/index.tsx
@@ -53,7 +53,7 @@ export default function LandingPage() {
return (
-
Fix It Local™ | 21st Century Service Directory
+ Fix-It-Local™ | 21st Century Service Directory
@@ -72,7 +72,7 @@ export default function LandingPage() {
Verified Professionals & AI-Powered Matching
- The Crafted Service Network
+ The Fix-It-Local Service Network
Find reliable, verified experts for your home or business. Real-time availability, transparent pricing, and zero spam.
diff --git a/frontend/src/pages/login.tsx b/frontend/src/pages/login.tsx
index 6172306..30cc857 100644
--- a/frontend/src/pages/login.tsx
+++ b/frontend/src/pages/login.tsx
@@ -1,4 +1,3 @@
-
import React, { useEffect, useState } from 'react';
import type { ReactElement } from 'react';
import Head from 'next/head';
@@ -20,6 +19,7 @@ import { useAppDispatch, useAppSelector } from '../stores/hooks';
import Link from 'next/link';
import {toast, ToastContainer} from "react-toastify";
import { getPexelsImage, getPexelsVideo } from '../helpers/pexels'
+import Logo from '../components/Logo'
export default function Login() {
const router = useRouter();
@@ -43,7 +43,7 @@ export default function Login() {
password: 'b2096650',
remember: true })
- const title = 'Fix It Local'
+ const title = 'Fix-It-Local'
// Fetch Pexels image/video
useEffect( () => {
@@ -171,12 +171,7 @@ export default function Login() {
{/* Branding */}
-
-
-
-
- Fix It Local™
-
+
Account Login
Enter your credentials to access your dashboard
@@ -270,7 +265,7 @@ export default function Login() {
- © 2026 Fix It Local™. All rights reserved.
+ © 2026 Fix-It-Local™. All rights reserved.
Privacy Policy
@@ -283,4 +278,4 @@ export default function Login() {
Login.getLayout = function getLayout(page: ReactElement) {
return