From 53225cc83093c7018571f5c518c5a4ba1d665013 Mon Sep 17 00:00:00 2001 From: FlashL3opard <69573060+Flashl3opard@users.noreply.github.com> Date: Fri, 10 Jul 2026 17:58:39 +0530 Subject: [PATCH 1/2] PHEE-380: add Login page and Settings page with accordion sections --- src/components/ui/accordion.tsx | 59 ++++++ src/components/ui/checkbox.tsx | 26 +++ src/components/ui/switch.tsx | 25 +++ src/modules/auth/Login.tsx | 142 +++++++++++++++ src/modules/settings/Settings.tsx | 293 ++++++++++++++++++++++++++++++ src/pages/LoginPage.tsx | 8 +- src/pages/Settings.tsx | 8 +- 7 files changed, 547 insertions(+), 14 deletions(-) create mode 100644 src/components/ui/accordion.tsx create mode 100644 src/components/ui/checkbox.tsx create mode 100644 src/components/ui/switch.tsx create mode 100644 src/modules/auth/Login.tsx create mode 100644 src/modules/settings/Settings.tsx 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..d9737ea --- /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..0c75a0b --- /dev/null +++ b/src/modules/settings/Settings.tsx @@ -0,0 +1,293 @@ +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) { + setLogoPreview(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' From 3259c129132e78cde78e50b7f0103af59d71ac67 Mon Sep 17 00:00:00 2001 From: FlashL3opard <69573060+Flashl3opard@users.noreply.github.com> Date: Fri, 10 Jul 2026 19:20:59 +0530 Subject: [PATCH 2/2] Addressed Copilot Suggestions --- src/modules/auth/Login.tsx | 2 +- src/modules/settings/Settings.tsx | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/modules/auth/Login.tsx b/src/modules/auth/Login.tsx index d9737ea..84e52ce 100644 --- a/src/modules/auth/Login.tsx +++ b/src/modules/auth/Login.tsx @@ -85,9 +85,9 @@ export default function Login() { /> diff --git a/src/modules/settings/Settings.tsx b/src/modules/settings/Settings.tsx index 0c75a0b..3618903 100644 --- a/src/modules/settings/Settings.tsx +++ b/src/modules/settings/Settings.tsx @@ -61,9 +61,11 @@ export default function Settings() { function handleLogoChange(e: React.ChangeEvent) { const file = e.target.files?.[0] - if (file) { - setLogoPreview(URL.createObjectURL(file)) - } + if (!file) return + setLogoPreview((prev) => { + if (prev) URL.revokeObjectURL(prev) + return URL.createObjectURL(file) + }) } return ( @@ -136,9 +138,9 @@ export default function Settings() {