diff --git a/src/components/ui/accordion.tsx b/src/components/ui/accordion.tsx new file mode 100644 index 0000000..aa0e29a --- /dev/null +++ b/src/components/ui/accordion.tsx @@ -0,0 +1,59 @@ +import * as React from "react" +import { Accordion as AccordionPrimitive } from "radix-ui" +import { ChevronDown } from "lucide-react" +import { cn } from "@/lib/utils" + +const Accordion = AccordionPrimitive.Root + +function AccordionItem({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AccordionTrigger({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + svg]:rotate-180", + className + )} + {...props} + > + {children} + + + + ) +} + +function AccordionContent({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + +
{children}
+
+ ) +} + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } diff --git a/src/components/ui/checkbox.tsx b/src/components/ui/checkbox.tsx new file mode 100644 index 0000000..e535c5d --- /dev/null +++ b/src/components/ui/checkbox.tsx @@ -0,0 +1,26 @@ +import * as React from "react" +import { Checkbox as CheckboxPrimitive } from "radix-ui" +import { CheckIcon } from "lucide-react" +import { cn } from "@/lib/utils" + +function Checkbox({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + + + ) +} + +export { Checkbox } diff --git a/src/components/ui/switch.tsx b/src/components/ui/switch.tsx new file mode 100644 index 0000000..9c353e3 --- /dev/null +++ b/src/components/ui/switch.tsx @@ -0,0 +1,25 @@ +import * as React from "react" +import { Switch as SwitchPrimitive } from "radix-ui" +import { cn } from "@/lib/utils" + +function Switch({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + ) +} + +export { Switch } diff --git a/src/modules/auth/Login.tsx b/src/modules/auth/Login.tsx new file mode 100644 index 0000000..84e52ce --- /dev/null +++ b/src/modules/auth/Login.tsx @@ -0,0 +1,142 @@ +import React, { useState } from 'react' +import { useNavigate } from 'react-router-dom' +import { User, Lock, Eye, EyeOff } from 'lucide-react' +import { Input } from '@/components/ui/input' +import { Button } from '@/components/ui/button' +import { Label } from '@/components/ui/label' +import { Checkbox } from '@/components/ui/checkbox' +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select' + +const TENANTS = ['greenbank', 'bluebank', 'redbank'] + +export default function Login() { + const navigate = useNavigate() + const [username, setUsername] = useState('') + const [password, setPassword] = useState('') + const [tenant, setTenant] = useState('') + const [rememberMe, setRememberMe] = useState(false) + const [showPassword, setShowPassword] = useState(false) + + function handleSubmit(e: React.FormEvent) { + e.preventDefault() + if (username === 'mifos' && password === 'password') { + navigate('/') + } else { + alert('Invalid credentials. Use mifos / password.') + } + } + + return ( +
+
+ {/* Logo + title */} +
+ Payment Hub EE { + e.currentTarget.style.display = 'none' + }} + /> +
+

Payment Hub EE

+

Operations

+
+
+ + {/* Form */} +
+ {/* Username */} +
+ +
+ + setUsername(e.target.value)} + className="pl-8" + required + /> +
+
+ + {/* Password */} +
+ +
+ + setPassword(e.target.value)} + className="pl-8 pr-9" + required + /> + +
+
+ + {/* Tenant */} +
+ + +
+ + {/* Remember me */} +
+ setRememberMe(Boolean(v))} + /> + +
+ + {/* Submit */} + +
+ + {/* Footer */} +

+ Powered by Mifos Initiative +

+
+
+ ) +} diff --git a/src/modules/settings/Settings.tsx b/src/modules/settings/Settings.tsx new file mode 100644 index 0000000..3618903 --- /dev/null +++ b/src/modules/settings/Settings.tsx @@ -0,0 +1,295 @@ +import React, { useState } from 'react' +import { Input } from '@/components/ui/input' +import { Label } from '@/components/ui/label' +import { Button } from '@/components/ui/button' +import { Switch } from '@/components/ui/switch' +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from '@/components/ui/accordion' +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select' +import { CheckCircle, ImageIcon } from 'lucide-react' + +const TENANTS = ['greenbank', 'bluebank', 'redbank'] +const FONTS = ['Inter', 'Roboto', 'Open Sans'] + +function SuccessAlert() { + return ( +
+ + Settings saved successfully +
+ ) +} + +export default function Settings() { + // Main Configuration + const [backendUrl, setBackendUrl] = useState('') + const [connectorUrl, setConnectorUrl] = useState('') + const [defaultTenant, setDefaultTenant] = useState('') + const [mainSaved, setMainSaved] = useState(false) + + // Images + const [logoPreview, setLogoPreview] = useState(null) + + // Theme + const [primaryColor, setPrimaryColor] = useState('#1565C0') + const [fontFamily, setFontFamily] = useState('') + const [darkMode, setDarkMode] = useState(false) + const [themeSaved, setThemeSaved] = useState(false) + + // Contact + const [supportEmail, setSupportEmail] = useState('') + const [helpDeskUrl, setHelpDeskUrl] = useState('') + const [orgName, setOrgName] = useState('') + const [contactSaved, setContactSaved] = useState(false) + + function saveWithFeedback( + setter: React.Dispatch> + ) { + setter(true) + setTimeout(() => setter(false), 2500) + } + + function handleLogoChange(e: React.ChangeEvent) { + const file = e.target.files?.[0] + if (!file) return + setLogoPreview((prev) => { + if (prev) URL.revokeObjectURL(prev) + return URL.createObjectURL(file) + }) + } + + return ( +
+

Settings

+ +
+ + {/* 1. Main Configuration */} + + Main Configuration + +
+ {mainSaved && } + +
+ + setBackendUrl(e.target.value)} + /> +
+ +
+ + setConnectorUrl(e.target.value)} + /> +
+ +
+ + +
+ + +
+
+
+ + {/* 2. Images */} + + Images + +
+ {/* Logo upload */} +
+ + +
+ + {/* Favicon upload */} +
+ + +
+ + {/* Preview */} + {logoPreview && ( +
+ +
+ Logo preview +
+
+ )} +
+
+
+ + {/* 3. Theme and Font */} + + Theme and Font + +
+ {themeSaved && } + +
+ +
+ setPrimaryColor(e.target.value)} + className="h-8 w-12 cursor-pointer rounded border border-input p-0.5" + /> + setPrimaryColor(e.target.value)} + className="w-32 font-mono" + placeholder="#1565C0" + /> +
+
+ +
+ + +
+ +
+ + +
+ + +
+
+
+ + {/* 4. Contact Information */} + + Contact Information + +
+ {contactSaved && } + +
+ + setSupportEmail(e.target.value)} + /> +
+ +
+ + setHelpDeskUrl(e.target.value)} + /> +
+ +
+ + setOrgName(e.target.value)} + /> +
+ + +
+
+
+
+
+
+ ) +} diff --git a/src/pages/LoginPage.tsx b/src/pages/LoginPage.tsx index 4131015..fec619c 100644 --- a/src/pages/LoginPage.tsx +++ b/src/pages/LoginPage.tsx @@ -1,7 +1 @@ -export default function LoginPage() { - return ( -
- Login -
- ) -} +export { default } from '@/modules/auth/Login' diff --git a/src/pages/Settings.tsx b/src/pages/Settings.tsx index 7a93e1a..4212537 100644 --- a/src/pages/Settings.tsx +++ b/src/pages/Settings.tsx @@ -1,7 +1 @@ -export default function Settings() { - return ( -
- Settings -
- ) -} +export { default } from '@/modules/settings/Settings'