Repository: memmcolapps/gridflex-frontend
Pull request #2: feat :Implemented code changes for login and forgot password page
Author: login: richardgidado
Created: 2025-04-23T12:39:48Z | Merged: 2025-04-24T10:55:53Z

feat :Implemented code changes for login and forgot password page


Diff:
diff --git a/package-lock.json b/package-lock.json
index 3dc81d9e..0f4c4a1d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,6 +8,7 @@
       "name": "gridflex",
       "version": "0.1.0",
       "dependencies": {
+        "@heroicons/react": "^2.2.0",
         "@t3-oss/env-nextjs": "^0.12.0",
         "next": "^15.2.3",
         "react": "^19.0.0",
@@ -214,6 +215,15 @@
         "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
       }
     },
+    "node_modules/@heroicons/react": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.2.0.tgz",
+      "integrity": "sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==",
+      "license": "MIT",
+      "peerDependencies": {
+        "react": ">= 16 || ^19.0.0-rc"
+      }
+    },
     "node_modules/@humanfs/core": {
       "version": "0.19.1",
       "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
diff --git a/package.json b/package.json
index ef055a60..6f74126c 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,7 @@
     "typecheck": "tsc --noEmit"
   },
   "dependencies": {
+    "@heroicons/react": "^2.2.0",
     "@t3-oss/env-nextjs": "^0.12.0",
     "next": "^15.2.3",
     "react": "^19.0.0",
diff --git a/public/images/gridflexlogo.jpg b/public/images/gridflexlogo.jpg
new file mode 100644
index 00000000..e919584b
Binary files /dev/null and b/public/images/gridflexlogo.jpg differ
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 338ef973..59ee9d11 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -15,11 +15,16 @@ const geist = Geist({
 });
 
 export default function RootLayout({
-  children,
+  children, 
 }: Readonly<{ children: React.ReactNode }>) {
   return (
     <html lang="en" className={`${geist.variable}`}>
-      <body>{children}</body>
+      <body>
+        <div className="md:hidden text-center text-gray-600">
+          <p></p>
+        </div>
+        {children}
+      </body>
     </html>
   );
 }
diff --git a/src/app/login/forgotpassword/page.tsx b/src/app/login/forgotpassword/page.tsx
new file mode 100644
index 00000000..b20ccc38
--- /dev/null
+++ b/src/app/login/forgotpassword/page.tsx
@@ -0,0 +1,29 @@
+"use client"
+
+import { Branding } from "../../../components/branding/branding";
+import { AuthhForm } from "../../../components/authform/authform";
+
+export default function ForgotPassword() {
+    return (
+        <div className="min-h-screen bg-white-100 flex items-center justify-center p-4">
+            <div className="hidden md:flex w-full max-w-5xl rounded-lg overflow-hidden h-120 gap-36">
+                {/* Left Section: Branding */}
+                <div className="w-1/2 text-black p-2 flex flex-col justify-between h-75">
+                    <Branding labelTitle="Forgot your password"
+                        label="No worries - enter your email to reset your password and get back to managaing your dashboard in no time" />
+                </div>
+
+                {/* Right Section: Sign-In Form */}
+
+                <div className="w-screen p-4 flex flex-col justify-center bg-white-700 h-60 md:mt-24 mr-2 rounded-lg shadow-[0px_0px_10px_0px_rgba(0,0,0,0.25)]">
+                    <AuthhForm formType="resetpassword"
+                        onSubmit={(e) => {
+                            e.preventDefault();
+                        }}
+                    />
+                </div>
+            </div>
+        </div>
+    );
+};
+
diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx
index 3731dddf..a644d665 100644
--- a/src/app/login/page.tsx
+++ b/src/app/login/page.tsx
@@ -1,9 +1,28 @@
 "use client";
+import { Branding } from "../../components/branding/branding";
+import { AuthhForm } from "../../components/authform/authform";
 
 export default function Login() {
   return (
-    <div className="flex min-h-screen items-center justify-center">
-      <div className="">Welcome to the login page</div>
+    <div className="min-h-screen bg-white-100 flex items-center justify-center p-4">
+      <div className="hidden md:flex w-full max-w-5xl rounded-lg overflow-hidden h-120 gap-36">
+        {/* Left Section: Branding */}
+        <div className="w-1/2 text-black p-2 flex flex-col justify-between h-75">
+          <Branding labelTitle="Welcome back!"
+            label=" Log in to access your centralized dashboard, where you can manage data, billing,
+              vending, and HES services—all in one streamlined platform." />
+        </div>
+
+        {/* Right Section: Sign-In Form */}
+        <div className="w-screen p-2 flex flex-col justify-center bg-white-700 h-85 shadow-[0px_0px_10px_0px_rgba(0,0,0,0.25)] mt-14 mr-2 rounded-lg">
+          <AuthhForm formType="signin"
+            onSubmit={(e) => {
+              e.preventDefault();
+            }} />
+        </div>
+      </div>
     </div>
   );
-}
+};
+
+
diff --git a/src/components/authform/authform.tsx b/src/components/authform/authform.tsx
new file mode 100644
index 00000000..00334c61
--- /dev/null
+++ b/src/components/authform/authform.tsx
@@ -0,0 +1,112 @@
+import { useState } from 'react';
+import Link from 'next/link';
+import { EyeIcon, EyeSlashIcon, ArrowLeftIcon } from '@heroicons/react/24/outline';
+
+type FormType = 'signin' | 'resetpassword';
+
+interface ReusableFormProps {
+    formType: FormType;
+    onSubmit: (e: React.FormEvent) => void;
+    initialEmail?: string;
+    initialPassword?: string;
+}
+
+export function AuthhForm({ formType, onSubmit, initialEmail = '', initialPassword = '' }: ReusableFormProps) {
+    const [email, setEmail] = useState(initialEmail);
+    const [password, setPassword] = useState(initialPassword);
+    const [showPassword, setShowPassword] = useState(false);
+
+    const isSignInForm = formType === 'signin';
+    const isFormValid = isSignInForm
+        ? email.trim() !== '' && password.trim() !== ''
+        : email.trim() !== '';
+
+    const togglePasswordVisibility = () => setShowPassword(!showPassword);
+
+    return (
+        <form
+            onSubmit={onSubmit}
+            className="space-y-4 p-4 rounded-lg flex flex-col align-center"
+        >
+            <h2 className="text-xl font-normal text-gray-800 mb-6 text-center">
+                {isSignInForm ? 'Sign In' : 'Reset Password'}
+            </h2>
+
+            <div>
+                <label htmlFor="email" className="block text-sm font-medium text-gray-700">
+                    Email
+                </label>
+                <input
+                    type="email"
+                    id="email"
+                    placeholder="Enter your email"
+                    value={email}
+                    onChange={(e) => setEmail(e.target.value)}
+                    className="mt-1 w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
+                    required
+                />
+            </div>
+
+            {isSignInForm && (
+                <>
+                    <div className="relative">
+                        <label htmlFor="password" className="block text-sm font-medium text-gray-700">
+                            Password
+                        </label>
+                        <input
+                            type={showPassword ? "text" : "password"}
+                            id="password"
+                            placeholder="Enter your password"
+                            value={password}
+                            onChange={(e) => setPassword(e.target.value)}
+                            className="mt-1 w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 pr-10"
+                            required
+                        />
+                        <button
+                            type="button"
+                            onClick={togglePasswordVisibility}
+                            className="absolute inset-y-0 right-0 flex items-center pr-3"
+                            aria-label={showPassword ? "Hide password" : "Show password"}
+                        >
+                            {showPassword ? (
+                                <EyeIcon className="h-5 w-5 text-gray-500 mt-6" />
+
+                            ) : (
+                                <EyeSlashIcon className="h-5 w-5 text-gray-500 mt-6" />
+                            )}
+                        </button>
+                    </div>
+
+                    <div className="flex justify-end">
+                        <Link href="/login/forgotpassword" className="text-sm text-black-600 hover:underline">
+                            Forgot Password?
+                        </Link>
+                    </div>
+                </>
+            )}
+
+            <button
+                type="submit"
+                disabled={!isFormValid}
+                className={`w-full py-2 rounded-md text-white transition-colors ${!isFormValid
+                        ? "bg-[#161CCA]/50 cursor-not-allowed"
+                        : "bg-[#161CCA] hover:bg-[#161CCA]/90"
+                    }`}
+            >
+                {isSignInForm ? 'Sign In' : 'Reset Password'}
+            </button>
+
+            {!isSignInForm && (
+                <div className="flex justify-center">
+                    <Link
+                        href="/login"
+                        className="flex items-center space-x-1 px-2 py-1.5 text-gray-700 bg-transparent rounded focus:outline-none"
+                    >
+                        <ArrowLeftIcon className="h-4 w-5 text-[#161CCA]" aria-hidden="true" />
+                        <span>Back to Login</span>
+                    </Link>
+                </div>
+            )}
+        </form>
+    );
+};
diff --git a/src/components/branding/branding.tsx b/src/components/branding/branding.tsx
new file mode 100644
index 00000000..d0a6fd48
--- /dev/null
+++ b/src/components/branding/branding.tsx
@@ -0,0 +1,24 @@
+interface BrandingProps{
+    labelTitle: string 
+    label: string   
+}
+export function Branding({labelTitle,label} :BrandingProps){
+    return(
+        <div className="w-1/2 text-black p-2 flex flex-col justify-between h-100">
+        <div className="w-95 h-100 flex items-start">
+            <img
+            className="ml-4"
+                src="/images/gridflexlogo.jpg"
+                alt="Gridflex logo"
+                width="220"
+                height="320"
+            />
+        </div>
+        <div className="w-95">
+        <h1 className="text-2xl font-semibold ml-4 mt-2">{labelTitle}</h1>
+        <p className="text-sm mt-4 ml-4 whitespace-pre-line text-gray-600">{label}</p>
+        </div>
+    </div>
+    )
+
+}
\ No newline at end of file


Reviews:

Review comments:

PR comments:
user: login: netlify[bot]
created_at: 2025-04-23T12:40:02Z
body: ### <span aria-hidden="true">✅</span> Deploy Preview for *gridflexfrontend* ready!


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | 2537c89121322fa2af79e82507c9de93e5a8075e |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/gridflexfrontend/deploys/680a1826c979680008fd4a96 |
|<span aria-hidden="true">😎</span> Deploy Preview | [https://deploy-preview-2--gridflexfrontend.netlify.app](https://deploy-preview-2--gridflexfrontend.netlify.app) |
|<span aria-hidden="true">📱</span> Preview on mobile | <details><summary> Toggle QR Code... </summary><br /><br />![QR Code](https://app.netlify.com/qr-code/eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJodHRwczovL2RlcGxveS1wcmV2aWV3LTItLWdyaWRmbGV4ZnJvbnRlbmQubmV0bGlmeS5hcHAifQ.t835rW6i45oHMY7InyeLorLRIgS_udyhhZ3FpDEjNYw)<br /><br />_Use your smartphone camera to open QR code link._</details> |
---
<!-- [gridflexfrontend Preview](https://deploy-preview-2--gridflexfrontend.netlify.app) -->
_To edit notification comments on pull requests, go to your [Netlify site configuration](https://app.netlify.com/sites/gridflexfrontend/configuration/notifications#deploy-webhooks)._
author_association: NONE
user: login: richardgidado
created_at: 2025-04-23T14:30:20Z
body: Update on layout for small screens
author_association: COLLABORATOR
user: login: richardgidado
created_at: 2025-04-24T10:26:39Z
body: Created resusable components for brandind and form,installed heroicons package,removed responsive warning messages 
author_association: COLLABORATOR
user: login: richardgidado
created_at: 2025-04-24T10:33:57Z
body: Added a reusable component for branding and forms for sign in and forgot password page,removed responsive warning messages,installed heroicons package,used an image instead of svg in the branding component
author_association: COLLABORATOR