Initial Claude Run

This commit is contained in:
Jake Kasper
2025-08-21 07:06:36 -05:00
parent 5ead64afcd
commit 2a46f7261e
53 changed files with 7633 additions and 2 deletions

View File

@@ -0,0 +1,14 @@
import React from 'react';
const AdminDashboard = () => {
return (
<div className="p-6">
<h1 className="text-2xl font-bold text-gray-900 mb-6">Admin Dashboard</h1>
<div className="card">
<p className="text-gray-600">Admin dashboard coming soon...</p>
</div>
</div>
);
};
export default AdminDashboard;

View File

@@ -0,0 +1,14 @@
import React from 'react';
const AdminProducts = () => {
return (
<div className="p-6">
<h1 className="text-2xl font-bold text-gray-900 mb-6">Manage Products</h1>
<div className="card">
<p className="text-gray-600">Product management coming soon...</p>
</div>
</div>
);
};
export default AdminProducts;

View File

@@ -0,0 +1,14 @@
import React from 'react';
const AdminUsers = () => {
return (
<div className="p-6">
<h1 className="text-2xl font-bold text-gray-900 mb-6">Manage Users</h1>
<div className="card">
<p className="text-gray-600">User management coming soon...</p>
</div>
</div>
);
};
export default AdminUsers;

View File

@@ -0,0 +1,14 @@
import React from 'react';
const ApplicationLog = () => {
return (
<div className="p-6">
<h1 className="text-2xl font-bold text-gray-900 mb-6">Log Application</h1>
<div className="card">
<p className="text-gray-600">Application logging coming soon...</p>
</div>
</div>
);
};
export default ApplicationLog;

View File

@@ -0,0 +1,14 @@
import React from 'react';
const ApplicationPlan = () => {
return (
<div className="p-6">
<h1 className="text-2xl font-bold text-gray-900 mb-6">Plan Application</h1>
<div className="card">
<p className="text-gray-600">Application planning coming soon...</p>
</div>
</div>
);
};
export default ApplicationPlan;

View File

@@ -0,0 +1,14 @@
import React from 'react';
const Applications = () => {
return (
<div className="p-6">
<h1 className="text-2xl font-bold text-gray-900 mb-6">Applications</h1>
<div className="card">
<p className="text-gray-600">Application management coming soon...</p>
</div>
</div>
);
};
export default Applications;

View File

@@ -0,0 +1,103 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { useForm } from 'react-hook-form';
import { useAuth } from '../../hooks/useAuth';
import LoadingSpinner from '../../components/UI/LoadingSpinner';
const ForgotPassword = () => {
const { forgotPassword, loading } = useAuth();
const {
register,
handleSubmit,
formState: { errors },
setError,
reset,
} = useForm();
const onSubmit = async (data) => {
const result = await forgotPassword(data.email);
if (result.success) {
reset();
} else {
setError('root', {
type: 'manual',
message: result.error
});
}
};
return (
<div className="space-y-6">
<div>
<h2 className="text-2xl font-bold text-gray-900">Forgot your password?</h2>
<p className="mt-2 text-sm text-gray-600">
Enter your email address and we'll send you a link to reset your password.
</p>
</div>
<form className="space-y-6" onSubmit={handleSubmit(onSubmit)}>
{errors.root && (
<div className="rounded-md bg-red-50 p-4">
<div className="flex">
<div className="ml-3">
<h3 className="text-sm font-medium text-red-800">
{errors.root.message}
</h3>
</div>
</div>
</div>
)}
<div>
<label htmlFor="email" className="label">
Email address
</label>
<div className="mt-1">
<input
id="email"
type="email"
autoComplete="email"
className={errors.email ? 'input-error' : 'input'}
{...register('email', {
required: 'Email is required',
pattern: {
value: /^\S+@\S+$/i,
message: 'Invalid email address',
},
})}
/>
{errors.email && (
<p className="mt-2 text-sm text-red-600">{errors.email.message}</p>
)}
</div>
</div>
<div>
<button
type="submit"
disabled={loading}
className="btn-primary w-full justify-center"
>
{loading ? (
<LoadingSpinner size="sm" color="white" />
) : (
'Send reset link'
)}
</button>
</div>
<div className="text-center">
<Link
to="/login"
className="font-medium text-primary-600 hover:text-primary-500"
>
Back to sign in
</Link>
</div>
</form>
</div>
);
};
export default ForgotPassword;

View File

@@ -0,0 +1,203 @@
import React, { useState } from 'react';
import { Link, useNavigate, useLocation } from 'react-router-dom';
import { useForm } from 'react-hook-form';
import { EyeIcon, EyeSlashIcon } from '@heroicons/react/24/outline';
import { useAuth } from '../../hooks/useAuth';
import LoadingSpinner from '../../components/UI/LoadingSpinner';
const Login = () => {
const [showPassword, setShowPassword] = useState(false);
const { login, loading } = useAuth();
const navigate = useNavigate();
const location = useLocation();
const from = location.state?.from?.pathname || '/dashboard';
const {
register,
handleSubmit,
formState: { errors },
setError,
} = useForm();
const onSubmit = async (data) => {
const result = await login(data);
if (result.success) {
navigate(from, { replace: true });
} else {
setError('root', {
type: 'manual',
message: result.error
});
}
};
const handleGoogleLogin = () => {
// Redirect to Google OAuth endpoint
window.location.href = `${process.env.REACT_APP_API_URL || 'http://localhost:5000'}/api/auth/google`;
};
return (
<div className="space-y-6">
<div>
<h2 className="text-2xl font-bold text-gray-900">Sign in to your account</h2>
<p className="mt-2 text-sm text-gray-600">
Or{' '}
<Link
to="/register"
className="font-medium text-primary-600 hover:text-primary-500"
>
create a new account
</Link>
</p>
</div>
<form className="space-y-6" onSubmit={handleSubmit(onSubmit)}>
{errors.root && (
<div className="rounded-md bg-red-50 p-4">
<div className="flex">
<div className="ml-3">
<h3 className="text-sm font-medium text-red-800">
{errors.root.message}
</h3>
</div>
</div>
</div>
)}
<div>
<label htmlFor="email" className="label">
Email address
</label>
<div className="mt-1">
<input
id="email"
type="email"
autoComplete="email"
className={errors.email ? 'input-error' : 'input'}
{...register('email', {
required: 'Email is required',
pattern: {
value: /^\S+@\S+$/i,
message: 'Invalid email address',
},
})}
/>
{errors.email && (
<p className="mt-2 text-sm text-red-600">{errors.email.message}</p>
)}
</div>
</div>
<div>
<label htmlFor="password" className="label">
Password
</label>
<div className="mt-1 relative">
<input
id="password"
type={showPassword ? 'text' : 'password'}
autoComplete="current-password"
className={errors.password ? 'input-error pr-10' : 'input pr-10'}
{...register('password', {
required: 'Password is required',
})}
/>
<button
type="button"
className="absolute inset-y-0 right-0 pr-3 flex items-center"
onClick={() => setShowPassword(!showPassword)}
>
{showPassword ? (
<EyeSlashIcon className="h-5 w-5 text-gray-400" />
) : (
<EyeIcon className="h-5 w-5 text-gray-400" />
)}
</button>
{errors.password && (
<p className="mt-2 text-sm text-red-600">{errors.password.message}</p>
)}
</div>
</div>
<div className="flex items-center justify-between">
<div className="flex items-center">
<input
id="remember-me"
name="remember-me"
type="checkbox"
className="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded"
/>
<label htmlFor="remember-me" className="ml-2 block text-sm text-gray-900">
Remember me
</label>
</div>
<div className="text-sm">
<Link
to="/forgot-password"
className="font-medium text-primary-600 hover:text-primary-500"
>
Forgot your password?
</Link>
</div>
</div>
<div>
<button
type="submit"
disabled={loading}
className="btn-primary w-full justify-center"
>
{loading ? (
<LoadingSpinner size="sm" color="white" />
) : (
'Sign in'
)}
</button>
</div>
<div className="mt-6">
<div className="relative">
<div className="absolute inset-0 flex items-center">
<div className="w-full border-t border-gray-300" />
</div>
<div className="relative flex justify-center text-sm">
<span className="px-2 bg-white text-gray-500">Or continue with</span>
</div>
</div>
<div className="mt-6">
<button
type="button"
onClick={handleGoogleLogin}
className="w-full inline-flex justify-center py-2 px-4 border border-gray-300 rounded-lg shadow-sm bg-white text-sm font-medium text-gray-500 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 transition-colors duration-200"
>
<svg className="w-5 h-5" viewBox="0 0 24 24">
<path
fill="currentColor"
d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"
/>
<path
fill="currentColor"
d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"
/>
<path
fill="currentColor"
d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"
/>
<path
fill="currentColor"
d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"
/>
</svg>
<span className="ml-2">Sign in with Google</span>
</button>
</div>
</div>
</form>
</div>
);
};
export default Login;

View File

@@ -0,0 +1,248 @@
import React, { useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { useForm } from 'react-hook-form';
import { EyeIcon, EyeSlashIcon } from '@heroicons/react/24/outline';
import { useAuth } from '../../hooks/useAuth';
import LoadingSpinner from '../../components/UI/LoadingSpinner';
const Register = () => {
const [showPassword, setShowPassword] = useState(false);
const { register: registerUser, loading } = useAuth();
const navigate = useNavigate();
const {
register,
handleSubmit,
formState: { errors },
watch,
setError,
} = useForm();
const password = watch('password');
const onSubmit = async (data) => {
const result = await registerUser(data);
if (result.success) {
navigate('/dashboard');
} else {
setError('root', {
type: 'manual',
message: result.error
});
}
};
return (
<div className="space-y-6">
<div>
<h2 className="text-2xl font-bold text-gray-900">Create your account</h2>
<p className="mt-2 text-sm text-gray-600">
Already have an account?{' '}
<Link
to="/login"
className="font-medium text-primary-600 hover:text-primary-500"
>
Sign in here
</Link>
</p>
</div>
<form className="space-y-6" onSubmit={handleSubmit(onSubmit)}>
{errors.root && (
<div className="rounded-md bg-red-50 p-4">
<div className="flex">
<div className="ml-3">
<h3 className="text-sm font-medium text-red-800">
{errors.root.message}
</h3>
</div>
</div>
</div>
)}
<div className="grid grid-cols-2 gap-4">
<div>
<label htmlFor="firstName" className="label">
First name
</label>
<div className="mt-1">
<input
id="firstName"
type="text"
autoComplete="given-name"
className={errors.firstName ? 'input-error' : 'input'}
{...register('firstName', {
required: 'First name is required',
minLength: {
value: 2,
message: 'First name must be at least 2 characters',
},
})}
/>
{errors.firstName && (
<p className="mt-2 text-sm text-red-600">{errors.firstName.message}</p>
)}
</div>
</div>
<div>
<label htmlFor="lastName" className="label">
Last name
</label>
<div className="mt-1">
<input
id="lastName"
type="text"
autoComplete="family-name"
className={errors.lastName ? 'input-error' : 'input'}
{...register('lastName', {
required: 'Last name is required',
minLength: {
value: 2,
message: 'Last name must be at least 2 characters',
},
})}
/>
{errors.lastName && (
<p className="mt-2 text-sm text-red-600">{errors.lastName.message}</p>
)}
</div>
</div>
</div>
<div>
<label htmlFor="email" className="label">
Email address
</label>
<div className="mt-1">
<input
id="email"
type="email"
autoComplete="email"
className={errors.email ? 'input-error' : 'input'}
{...register('email', {
required: 'Email is required',
pattern: {
value: /^\S+@\S+$/i,
message: 'Invalid email address',
},
})}
/>
{errors.email && (
<p className="mt-2 text-sm text-red-600">{errors.email.message}</p>
)}
</div>
</div>
<div>
<label htmlFor="password" className="label">
Password
</label>
<div className="mt-1 relative">
<input
id="password"
type={showPassword ? 'text' : 'password'}
autoComplete="new-password"
className={errors.password ? 'input-error pr-10' : 'input pr-10'}
{...register('password', {
required: 'Password is required',
minLength: {
value: 8,
message: 'Password must be at least 8 characters',
},
pattern: {
value: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]/,
message: 'Password must contain uppercase, lowercase, number, and special character',
},
})}
/>
<button
type="button"
className="absolute inset-y-0 right-0 pr-3 flex items-center"
onClick={() => setShowPassword(!showPassword)}
>
{showPassword ? (
<EyeSlashIcon className="h-5 w-5 text-gray-400" />
) : (
<EyeIcon className="h-5 w-5 text-gray-400" />
)}
</button>
{errors.password && (
<p className="mt-2 text-sm text-red-600">{errors.password.message}</p>
)}
</div>
</div>
<div>
<label htmlFor="confirmPassword" className="label">
Confirm password
</label>
<div className="mt-1">
<input
id="confirmPassword"
type="password"
autoComplete="new-password"
className={errors.confirmPassword ? 'input-error' : 'input'}
{...register('confirmPassword', {
required: 'Please confirm your password',
validate: (value) =>
value === password || 'Passwords do not match',
})}
/>
{errors.confirmPassword && (
<p className="mt-2 text-sm text-red-600">{errors.confirmPassword.message}</p>
)}
</div>
</div>
<div className="flex items-center">
<input
id="agree-terms"
type="checkbox"
className="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 rounded"
{...register('agreeTerms', {
required: 'You must agree to the terms and conditions',
})}
/>
<label htmlFor="agree-terms" className="ml-2 block text-sm text-gray-900">
I agree to the{' '}
<Link
to="/terms"
className="text-primary-600 hover:text-primary-500"
target="_blank"
>
Terms and Conditions
</Link>{' '}
and{' '}
<Link
to="/privacy"
className="text-primary-600 hover:text-primary-500"
target="_blank"
>
Privacy Policy
</Link>
</label>
</div>
{errors.agreeTerms && (
<p className="text-sm text-red-600">{errors.agreeTerms.message}</p>
)}
<div>
<button
type="submit"
disabled={loading}
className="btn-primary w-full justify-center"
>
{loading ? (
<LoadingSpinner size="sm" color="white" />
) : (
'Create account'
)}
</button>
</div>
</form>
</div>
);
};
export default Register;

View File

@@ -0,0 +1,309 @@
import React from 'react';
import { Link } from 'react-router-dom';
import {
MapIcon,
WrenchScrewdriverIcon,
BeakerIcon,
CalendarDaysIcon,
ClockIcon,
CloudIcon,
PlusIcon,
ArrowTrendingUpIcon,
} from '@heroicons/react/24/outline';
import { useAuth } from '../../hooks/useAuth';
const Dashboard = () => {
const { user } = useAuth();
const quickActions = [
{
name: 'Add Property',
href: '/properties',
icon: MapIcon,
description: 'Set up a new lawn area',
color: 'bg-blue-500',
},
{
name: 'Plan Application',
href: '/applications/plan',
icon: CalendarDaysIcon,
description: 'Schedule a treatment',
color: 'bg-green-500',
},
{
name: 'Add Equipment',
href: '/equipment',
icon: WrenchScrewdriverIcon,
description: 'Register new equipment',
color: 'bg-purple-500',
},
{
name: 'Log Application',
href: '/applications/log',
icon: ClockIcon,
description: 'Record completed work',
color: 'bg-orange-500',
},
];
const stats = [
{
name: 'Properties',
value: '3',
change: '+1',
changeType: 'positive',
icon: MapIcon,
},
{
name: 'This Month\'s Applications',
value: '12',
change: '+4',
changeType: 'positive',
icon: CalendarDaysIcon,
},
{
name: 'Equipment Items',
value: '8',
change: '+2',
changeType: 'positive',
icon: WrenchScrewdriverIcon,
},
{
name: 'Products Used',
value: '15',
change: '+3',
changeType: 'positive',
icon: BeakerIcon,
},
];
const recentActivity = [
{
id: 1,
type: 'application',
title: 'Applied fertilizer to Front Yard',
property: 'Main Property',
date: '2 hours ago',
status: 'completed',
},
{
id: 2,
type: 'plan',
title: 'Scheduled weed control treatment',
property: 'Back Yard',
date: 'Tomorrow at 9:00 AM',
status: 'planned',
},
{
id: 3,
type: 'equipment',
title: 'Added new broadcast spreader',
property: null,
date: '3 days ago',
status: 'completed',
},
];
const upcomingTasks = [
{
id: 1,
title: 'Pre-emergent herbicide application',
property: 'Main Property - Front Yard',
date: 'March 15, 2024',
weather: 'Partly cloudy, 65°F',
priority: 'high',
},
{
id: 2,
title: 'Spring fertilizer application',
property: 'Side Property - Back Lawn',
date: 'March 20, 2024',
weather: 'Sunny, 72°F',
priority: 'medium',
},
];
return (
<div className="p-6 max-w-7xl mx-auto">
{/* Header */}
<div className="mb-8">
<h1 className="text-3xl font-bold text-gray-900">
Welcome back, {user?.firstName}!
</h1>
<p className="mt-2 text-gray-600">
Here's what's happening with your lawn care today.
</p>
</div>
{/* Stats Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
{stats.map((stat) => (
<div key={stat.name} className="card">
<div className="flex items-center">
<div className="flex-shrink-0">
<stat.icon className="h-8 w-8 text-gray-400" />
</div>
<div className="ml-4 flex-1">
<p className="text-sm font-medium text-gray-500">{stat.name}</p>
<div className="flex items-baseline">
<p className="text-2xl font-semibold text-gray-900">{stat.value}</p>
<span className={`ml-2 text-sm font-medium ${
stat.changeType === 'positive' ? 'text-green-600' : 'text-red-600'
}`}>
{stat.change}
</span>
</div>
</div>
</div>
</div>
))}
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
{/* Quick Actions */}
<div className="lg:col-span-1">
<div className="card">
<div className="card-header">
<h3 className="text-lg font-medium text-gray-900">Quick Actions</h3>
<p className="text-sm text-gray-500">Get started with common tasks</p>
</div>
<div className="space-y-3">
{quickActions.map((action) => (
<Link
key={action.name}
to={action.href}
className="flex items-center p-3 rounded-lg border border-gray-200 hover:border-gray-300 hover:shadow-sm transition-all duration-200"
>
<div className={`flex-shrink-0 p-2 rounded-lg ${action.color}`}>
<action.icon className="h-5 w-5 text-white" />
</div>
<div className="ml-3 flex-1">
<p className="text-sm font-medium text-gray-900">{action.name}</p>
<p className="text-xs text-gray-500">{action.description}</p>
</div>
<PlusIcon className="h-4 w-4 text-gray-400" />
</Link>
))}
</div>
</div>
</div>
{/* Recent Activity & Upcoming Tasks */}
<div className="lg:col-span-2 space-y-8">
{/* Recent Activity */}
<div className="card">
<div className="card-header">
<h3 className="text-lg font-medium text-gray-900">Recent Activity</h3>
<Link
to="/history"
className="text-sm text-primary-600 hover:text-primary-500"
>
View all
</Link>
</div>
<div className="space-y-4">
{recentActivity.map((activity) => (
<div key={activity.id} className="flex items-start space-x-3">
<div className={`flex-shrink-0 p-1 rounded-full ${
activity.status === 'completed' ? 'bg-green-100' : 'bg-blue-100'
}`}>
<div className={`h-2 w-2 rounded-full ${
activity.status === 'completed' ? 'bg-green-600' : 'bg-blue-600'
}`} />
</div>
<div className="flex-1 min-w-0">
<p className="text-sm text-gray-900">{activity.title}</p>
{activity.property && (
<p className="text-xs text-gray-500">{activity.property}</p>
)}
<p className="text-xs text-gray-400">{activity.date}</p>
</div>
</div>
))}
</div>
</div>
{/* Upcoming Tasks */}
<div className="card">
<div className="card-header">
<h3 className="text-lg font-medium text-gray-900">Upcoming Tasks</h3>
<Link
to="/applications"
className="text-sm text-primary-600 hover:text-primary-500"
>
View all
</Link>
</div>
<div className="space-y-4">
{upcomingTasks.map((task) => (
<div key={task.id} className="border border-gray-200 rounded-lg p-4">
<div className="flex items-start justify-between">
<div className="flex-1">
<h4 className="text-sm font-medium text-gray-900">{task.title}</h4>
<p className="text-xs text-gray-500 mt-1">{task.property}</p>
<div className="flex items-center mt-2 space-x-4">
<span className="text-xs text-gray-500">{task.date}</span>
<div className="flex items-center space-x-1">
<CloudIcon className="h-3 w-3 text-gray-400" />
<span className="text-xs text-gray-500">{task.weather}</span>
</div>
</div>
</div>
<span className={`inline-flex items-center px-2 py-1 rounded-full text-xs font-medium ${
task.priority === 'high'
? 'bg-red-100 text-red-800'
: task.priority === 'medium'
? 'bg-yellow-100 text-yellow-800'
: 'bg-green-100 text-green-800'
}`}>
{task.priority}
</span>
</div>
</div>
))}
</div>
</div>
</div>
</div>
{/* Weather Widget */}
<div className="mt-8">
<div className="card">
<div className="flex items-center justify-between mb-4">
<h3 className="text-lg font-medium text-gray-900">Today's Weather</h3>
<Link
to="/weather"
className="text-sm text-primary-600 hover:text-primary-500"
>
Detailed forecast
</Link>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="flex items-center space-x-3">
<CloudIcon className="h-8 w-8 text-blue-500" />
<div>
<p className="text-2xl font-semibold text-gray-900">72°F</p>
<p className="text-sm text-gray-500">Partly cloudy</p>
</div>
</div>
<div className="flex items-center justify-between">
<span className="text-sm text-gray-500">Wind:</span>
<span className="text-sm font-medium text-gray-900">5 mph</span>
</div>
<div className="flex items-center justify-between">
<span className="text-sm text-gray-500">Humidity:</span>
<span className="text-sm font-medium text-gray-900">45%</span>
</div>
</div>
<div className="mt-4 p-3 bg-green-50 rounded-lg">
<p className="text-sm text-green-800">
Good conditions for lawn applications today
</p>
</div>
</div>
</div>
</div>
);
};
export default Dashboard;

View File

@@ -0,0 +1,14 @@
import React from 'react';
const Equipment = () => {
return (
<div className="p-6">
<h1 className="text-2xl font-bold text-gray-900 mb-6">Equipment</h1>
<div className="card">
<p className="text-gray-600">Equipment management coming soon...</p>
</div>
</div>
);
};
export default Equipment;

View File

@@ -0,0 +1,46 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { HomeIcon } from '@heroicons/react/24/outline';
const NotFound = () => {
return (
<div className="min-h-screen bg-white flex flex-col justify-center items-center px-4 sm:px-6 lg:px-8">
<div className="max-w-md w-full space-y-8 text-center">
<div>
<h1 className="text-9xl font-bold text-primary-600">404</h1>
<h2 className="mt-6 text-3xl font-bold text-gray-900">
Page not found
</h2>
<p className="mt-2 text-sm text-gray-600">
Sorry, we couldn't find the page you're looking for.
</p>
</div>
<div className="mt-8 space-y-4">
<Link
to="/dashboard"
className="btn-primary w-full justify-center"
>
<HomeIcon className="h-5 w-5 mr-2" />
Go back home
</Link>
<Link
to="/properties"
className="btn-outline w-full justify-center"
>
View Properties
</Link>
</div>
<div className="mt-8">
<p className="text-xs text-gray-500">
If you believe this is an error, please contact support.
</p>
</div>
</div>
</div>
);
};
export default NotFound;

View File

@@ -0,0 +1,48 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { ShieldExclamationIcon, HomeIcon } from '@heroicons/react/24/outline';
const Unauthorized = () => {
return (
<div className="min-h-screen bg-white flex flex-col justify-center items-center px-4 sm:px-6 lg:px-8">
<div className="max-w-md w-full space-y-8 text-center">
<div>
<div className="mx-auto h-24 w-24 bg-red-100 rounded-full flex items-center justify-center mb-6">
<ShieldExclamationIcon className="h-12 w-12 text-red-600" />
</div>
<h2 className="text-3xl font-bold text-gray-900">
Access Denied
</h2>
<p className="mt-2 text-sm text-gray-600">
You don't have permission to access this page. Contact your administrator if you believe this is an error.
</p>
</div>
<div className="mt-8 space-y-4">
<Link
to="/dashboard"
className="btn-primary w-full justify-center"
>
<HomeIcon className="h-5 w-5 mr-2" />
Go back home
</Link>
<Link
to="/profile"
className="btn-outline w-full justify-center"
>
View Profile
</Link>
</div>
<div className="mt-8">
<p className="text-xs text-gray-500">
Need help? Contact support for assistance.
</p>
</div>
</div>
</div>
);
};
export default Unauthorized;

View File

@@ -0,0 +1,14 @@
import React from 'react';
const History = () => {
return (
<div className="p-6">
<h1 className="text-2xl font-bold text-gray-900 mb-6">History</h1>
<div className="card">
<p className="text-gray-600">Application history coming soon...</p>
</div>
</div>
);
};
export default History;

View File

@@ -0,0 +1,14 @@
import React from 'react';
const Products = () => {
return (
<div className="p-6">
<h1 className="text-2xl font-bold text-gray-900 mb-6">Products</h1>
<div className="card">
<p className="text-gray-600">Product management coming soon...</p>
</div>
</div>
);
};
export default Products;

View File

@@ -0,0 +1,14 @@
import React from 'react';
const Profile = () => {
return (
<div className="p-6">
<h1 className="text-2xl font-bold text-gray-900 mb-6">Profile</h1>
<div className="card">
<p className="text-gray-600">Profile management coming soon...</p>
</div>
</div>
);
};
export default Profile;

View File

@@ -0,0 +1,14 @@
import React from 'react';
const Properties = () => {
return (
<div className="p-6">
<h1 className="text-2xl font-bold text-gray-900 mb-6">Properties</h1>
<div className="card">
<p className="text-gray-600">Property management coming soon...</p>
</div>
</div>
);
};
export default Properties;

View File

@@ -0,0 +1,17 @@
import React from 'react';
import { useParams } from 'react-router-dom';
const PropertyDetail = () => {
const { id } = useParams();
return (
<div className="p-6">
<h1 className="text-2xl font-bold text-gray-900 mb-6">Property Details</h1>
<div className="card">
<p className="text-gray-600">Property {id} details coming soon...</p>
</div>
</div>
);
};
export default PropertyDetail;

View File

@@ -0,0 +1,14 @@
import React from 'react';
const Weather = () => {
return (
<div className="p-6">
<h1 className="text-2xl font-bold text-gray-900 mb-6">Weather</h1>
<div className="card">
<p className="text-gray-600">Weather information coming soon...</p>
</div>
</div>
);
};
export default Weather;