} /> -
+
+
@@ -97,7 +129,8 @@ function App() { path="/transactions" element={ -
+
+
@@ -107,7 +140,8 @@ function App() { path="/treasury" element={ -
+
+
@@ -117,7 +151,8 @@ function App() { path="/reports" element={ -
+
+
diff --git a/apps/web/src/components/Breadcrumbs.tsx b/apps/web/src/components/Breadcrumbs.tsx new file mode 100644 index 0000000..1ce17d6 --- /dev/null +++ b/apps/web/src/components/Breadcrumbs.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { Link, useLocation } from 'react-router-dom'; +import { FiChevronRight, FiHome } from 'react-icons/fi'; + +export function Breadcrumbs() { + const location = useLocation(); + const paths = location.pathname.split('/').filter(Boolean); + const breadcrumbMap: Record = { + transactions: 'Transactions', + treasury: 'Treasury', + reports: 'Reports', + }; + return ( + + ); +} diff --git a/apps/web/src/components/FormField.tsx b/apps/web/src/components/FormField.tsx new file mode 100644 index 0000000..e86b11a --- /dev/null +++ b/apps/web/src/components/FormField.tsx @@ -0,0 +1,154 @@ +/** + * Enhanced Form Field Component + * With validation, inline help, and progress indicators + */ + +import React, { useState } from 'react'; +import { FiAlertCircle, FiCheckCircle, FiHelpCircle, FiEye, FiEyeOff } from 'react-icons/fi'; + +interface FormFieldProps { + label: string; + name: string; + type?: string; + value: string; + onChange: (e: React.ChangeEvent) => void; + error?: string; + helpText?: string; + required?: boolean; + placeholder?: string; + validation?: (value: string) => string | null; + showProgress?: boolean; + progressValue?: number; + options?: { value: string; label: string }[]; +} + +export function FormField({ + label, + name, + type = 'text', + value, + onChange, + error, + helpText, + required, + placeholder, + validation, + showProgress, + progressValue, + options, +}: FormFieldProps) { + const [showPassword, setShowPassword] = useState(false); + const [touched, setTouched] = useState(false); + const [localError, setLocalError] = useState(null); + + const handleBlur = () => { + setTouched(true); + if (validation) { + const validationError = validation(value); + setLocalError(validationError || null); + } + }; + + const displayError = error || (touched && localError); + const isValid = touched && !displayError && value; + + const inputClasses = `w-full px-3 py-2 border rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition ${ + displayError + ? 'border-red-500 focus:ring-red-500' + : isValid + ? 'border-green-500' + : 'border-gray-300' + }`; + + return ( +
+ + +
+ {type === 'select' && options ? ( + + ) : type === 'textarea' ? ( +