-
Notifications
You must be signed in to change notification settings - Fork 0
Implement Auth UI & Layout ( Sign In and Sign Up Page ) #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| import Image from "next/image" | ||
| import Link from "next/link" | ||
|
|
||
| const Layout = ({children} : {children:React.ReactNode} ) => { | ||
| return ( | ||
| <main className="auth-layout"> | ||
| <section className="auth-left-section scrollbar-hide-default"> | ||
| <Link href='/' className="auth-logo"> | ||
| <Image src="/assets/icons/logo.svg" alt="App-logo" width={140} height={32} className="h-8 w-auto" /> | ||
| </Link> | ||
|
|
||
| <div className="pb-6 lg:pb-8 flex-1"> | ||
| {children} | ||
| </div> | ||
| </section> | ||
|
|
||
| <section className="auth-right-section"> | ||
| <div className="z-10 relative lg:mt-4 lg:mb-16"> | ||
| <blockquote className="auth-blockquote"> | ||
| "Be fearful when others are greedy, and greedy when others are fearful." | ||
|
|
||
| </blockquote> | ||
| <div className="flex items-center justify-between"> | ||
| <div> | ||
| <cite className="auth-testimonial-author">— Warren Buffett</cite> | ||
| <p className="max-md:text-xs text-gray-500">Chairman and CEO of Berkshire Hathaway</p> | ||
| </div> | ||
| </div> | ||
| </div> | ||
|
|
||
| <div className="flex-1 relative"> | ||
| <Image src="/assets/images/dashboard.png" alt="Dashboard" width={1440} height={1150} className="auth-dashboard-preview absolute top-0"/> | ||
| </div> | ||
| </section> | ||
| </main> | ||
| ) | ||
| } | ||
| export default Layout |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| 'use client' | ||
| import React from 'react' | ||
| import InputField from '@/components/forms/InputField' | ||
| import FooterLink from '@/components/forms/FooterLink' | ||
| import { Button } from '@/components/ui/button' | ||
| import { SubmitHandler, useForm } from 'react-hook-form' | ||
|
|
||
| type SignInFormData = { | ||
| email: string | ||
| password: string | ||
| } | ||
|
|
||
| const SignIn = () => { | ||
| const { | ||
| register, | ||
| handleSubmit, | ||
| formState: { errors, isSubmitting }, | ||
| } = useForm<SignInFormData>({ | ||
| defaultValues: { | ||
| email: '', | ||
| password: '', | ||
| }, | ||
| mode: 'onBlur' | ||
| }) | ||
|
|
||
| const onSubmit: SubmitHandler<SignInFormData> = async (data) => { | ||
| try { | ||
| console.log(data) | ||
| } catch (e) { | ||
| console.error(e) | ||
| } | ||
| } | ||
|
|
||
| return ( | ||
| <> | ||
| <h1 className='form-title'>Welcome Back</h1> | ||
|
|
||
| <form onSubmit={handleSubmit(onSubmit)} className='space-y-5'> | ||
| <InputField | ||
| name="email" | ||
| label="Email" | ||
| placeholder="contact@example.com" | ||
| register={register} | ||
| error={errors.email} | ||
| validation={{ required: 'Email is required', pattern: { value: /^\w+@\w+\.\w+$/, message: 'Invalid email format' } }} | ||
| /> | ||
|
|
||
| <InputField | ||
| name="password" | ||
| label="Password" | ||
| placeholder="Enter your password" | ||
| type="password" | ||
| register={register} | ||
| error={errors.password} | ||
| validation={{ required: 'Password is required' }} | ||
| /> | ||
|
|
||
| <Button type='submit' disabled={isSubmitting} className='yellow-btn w-full mt-5'> | ||
| {isSubmitting ? 'Signing In...' : 'Sign In'} | ||
| </Button> | ||
|
|
||
| <FooterLink text="Don't have an account?" linkText='Sign-up' href='/sign-up' /> | ||
| </form> | ||
| </> | ||
| ) | ||
| } | ||
|
|
||
| export default SignIn | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,123 @@ | ||||||||||||||||||||||
| 'use client' | ||||||||||||||||||||||
| import { CountrySelectField } from '@/components/forms/CountrySelectField'; | ||||||||||||||||||||||
| import FooterLink from '@/components/forms/FooterLink'; | ||||||||||||||||||||||
| import InputField from '@/components/forms/InputField'; | ||||||||||||||||||||||
| import SelectField from '@/components/forms/SelectField'; | ||||||||||||||||||||||
| import { Button } from '@/components/ui/button'; | ||||||||||||||||||||||
| import { INVESTMENT_GOALS, PREFERRED_INDUSTRIES, RISK_TOLERANCE_OPTIONS } from '@/lib/constants'; | ||||||||||||||||||||||
| import { SubmitHandler, useForm } from 'react-hook-form' | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const SignUp = () => { | ||||||||||||||||||||||
| const { | ||||||||||||||||||||||
| register, | ||||||||||||||||||||||
| handleSubmit, | ||||||||||||||||||||||
| watch, | ||||||||||||||||||||||
| control, | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| formState: { errors, isSubmitting }, | ||||||||||||||||||||||
| } = useForm<SignUpFormData>({ | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| defaultValues: { | ||||||||||||||||||||||
| fullName: '', | ||||||||||||||||||||||
| email: '', | ||||||||||||||||||||||
| password: '', | ||||||||||||||||||||||
| country: 'INDIA', | ||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Incorrect country default value format. The default value Apply this diff to fix the country code: defaultValues: {
fullName: '',
email: '',
password: '',
- country: 'INDIA',
+ country: 'IN',
investmentGoals: 'Growth',
riskTolerance: 'Medium',
preferredIndustry: 'Technology'
},📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||
| investmentGoals: 'Growth', | ||||||||||||||||||||||
| riskTolerance: 'Medium', | ||||||||||||||||||||||
| preferredIndustry: 'Technology' | ||||||||||||||||||||||
| }, | ||||||||||||||||||||||
| mode: 'onBlur' | ||||||||||||||||||||||
| }, ); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const onSubmit = async(data: SignUpFormData) => { | ||||||||||||||||||||||
| try { | ||||||||||||||||||||||
| console.log(data); | ||||||||||||||||||||||
| }catch (e){ | ||||||||||||||||||||||
| console.error(e); | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| return ( | ||||||||||||||||||||||
| <> | ||||||||||||||||||||||
| <h1 className='form-title'>Sign Up & Personalize</h1> | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| <form onSubmit={handleSubmit(onSubmit)} className='space-y-5'> | ||||||||||||||||||||||
| {/* Input */} | ||||||||||||||||||||||
| <InputField | ||||||||||||||||||||||
| name="fullName" | ||||||||||||||||||||||
| label="Full Name" | ||||||||||||||||||||||
| placeholder="John Doe" | ||||||||||||||||||||||
| register={register} | ||||||||||||||||||||||
| error={errors.fullName} | ||||||||||||||||||||||
| validation={{ required: 'Full name is required', minLength: 2 }} | ||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add validation message for minLength. The Apply this diff to add a validation message: - validation={{ required: 'Full name is required', minLength: 2 }}
+ validation={{ required: 'Full name is required', minLength: { value: 2, message: 'Full name must be at least 2 characters' } }}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||
| /> | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| <InputField | ||||||||||||||||||||||
| name="email" | ||||||||||||||||||||||
| label="Email" | ||||||||||||||||||||||
| placeholder="contact@jsmastery.com" | ||||||||||||||||||||||
| register={register} | ||||||||||||||||||||||
| error={errors.email} | ||||||||||||||||||||||
| validation={{ required: 'Email name is required', pattern: /^\w+@\w+\.\w+$/, message: 'Email address is required' }} | ||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix malformed email validation pattern. The validation object structure is incorrect. The Apply this diff to fix the validation: - validation={{ required: 'Email name is required', pattern: /^\w+@\w+\.\w+$/, message: 'Email address is required' }}
+ validation={{ required: 'Email is required', pattern: { value: /^\w+@[^\s@]+\.[^\s@]+$/, message: 'Invalid email format' } }}Note: Also improved the regex pattern to better match email formats and fixed the typo "Email name" → "Email". 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||
| /> | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| <InputField | ||||||||||||||||||||||
| name="password" | ||||||||||||||||||||||
| label="Password" | ||||||||||||||||||||||
| placeholder="Enter a strong password" | ||||||||||||||||||||||
| type="password" | ||||||||||||||||||||||
| register={register} | ||||||||||||||||||||||
| error={errors.password} | ||||||||||||||||||||||
| validation={{ required: 'Password is required', minLength: 8 }} | ||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add validation message for minLength. Similar to the fullName field, the password minLength validation lacks a message. Apply this diff to add a validation message: - validation={{ required: 'Password is required', minLength: 8 }}
+ validation={{ required: 'Password is required', minLength: { value: 8, message: 'Password must be at least 8 characters' } }}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||
| /> | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| <CountrySelectField | ||||||||||||||||||||||
| name="country" | ||||||||||||||||||||||
| label="Country" | ||||||||||||||||||||||
| control={control} | ||||||||||||||||||||||
| error={errors.country} | ||||||||||||||||||||||
| required | ||||||||||||||||||||||
| /> | ||||||||||||||||||||||
| <SelectField | ||||||||||||||||||||||
| name="investmentGoals" | ||||||||||||||||||||||
| label="Investment Goals" | ||||||||||||||||||||||
| placeholder="Select your investment goal" | ||||||||||||||||||||||
| options={INVESTMENT_GOALS} | ||||||||||||||||||||||
| control={control} | ||||||||||||||||||||||
| error={errors.investmentGoals} | ||||||||||||||||||||||
| required | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /> | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| <SelectField | ||||||||||||||||||||||
| name="riskTolerance" | ||||||||||||||||||||||
| label="Risk Tolerance" | ||||||||||||||||||||||
| placeholder="Select your risk level" | ||||||||||||||||||||||
| options={RISK_TOLERANCE_OPTIONS} | ||||||||||||||||||||||
| control={control} | ||||||||||||||||||||||
| error={errors.riskTolerance} | ||||||||||||||||||||||
| required | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /> | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| <SelectField | ||||||||||||||||||||||
| name="preferredIndustry" | ||||||||||||||||||||||
| label="Preferred Industry" | ||||||||||||||||||||||
| placeholder="Select your preferred industry" | ||||||||||||||||||||||
| options={PREFERRED_INDUSTRIES} | ||||||||||||||||||||||
| control={control} | ||||||||||||||||||||||
| error={errors.preferredIndustry} | ||||||||||||||||||||||
| required | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /> | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| <Button type='submit' disabled={isSubmitting} className='yellow-btn w-full mt-5'> | ||||||||||||||||||||||
| {isSubmitting ? 'Creating Account' : 'Start Your Investing Journey'} | ||||||||||||||||||||||
| </Button> | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| <FooterLink text='Already Have an Account ?' linkText='Sign-in' href='/sign-in' /> | ||||||||||||||||||||||
| </form> | ||||||||||||||||||||||
| </> | ||||||||||||||||||||||
| ) | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| export default SignUp | ||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,146 @@ | ||||||||||||||||||||||||||||||||||||||||||||
| /* eslint-disable @typescript-eslint/no-explicit-any */ | ||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Avoid disabling TypeScript checks. The Apply this diff to properly type the control: -/* eslint-disable @typescript-eslint/no-explicit-any */
'use client';And update the type definition: type CountrySelectProps = {
name: string;
label: string;
- control: Control<any>;
+ control: Control<SignUpFormData>; // or use a generic type parameter
error?: FieldError;
required?: boolean;
};📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||
| 'use client'; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| import { useState } from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||
| import { Control, Controller, FieldError } from 'react-hook-form'; | ||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||
| Popover, | ||||||||||||||||||||||||||||||||||||||||||||
| PopoverContent, | ||||||||||||||||||||||||||||||||||||||||||||
| PopoverTrigger, | ||||||||||||||||||||||||||||||||||||||||||||
| } from '@/components/ui/popover'; | ||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||
| Command, | ||||||||||||||||||||||||||||||||||||||||||||
| CommandEmpty, | ||||||||||||||||||||||||||||||||||||||||||||
| CommandGroup, | ||||||||||||||||||||||||||||||||||||||||||||
| CommandInput, | ||||||||||||||||||||||||||||||||||||||||||||
| CommandItem, | ||||||||||||||||||||||||||||||||||||||||||||
| CommandList, | ||||||||||||||||||||||||||||||||||||||||||||
| } from '@/components/ui/command'; | ||||||||||||||||||||||||||||||||||||||||||||
| import { Button } from '@/components/ui/button'; | ||||||||||||||||||||||||||||||||||||||||||||
| import { Label } from '@/components/ui/label'; | ||||||||||||||||||||||||||||||||||||||||||||
| import { Check, ChevronsUpDown } from 'lucide-react'; | ||||||||||||||||||||||||||||||||||||||||||||
| import { cn } from '@/lib/utils'; | ||||||||||||||||||||||||||||||||||||||||||||
| import countryList from 'react-select-country-list'; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| type CountrySelectProps = { | ||||||||||||||||||||||||||||||||||||||||||||
| name: string; | ||||||||||||||||||||||||||||||||||||||||||||
| label: string; | ||||||||||||||||||||||||||||||||||||||||||||
| control: Control<any>; | ||||||||||||||||||||||||||||||||||||||||||||
| error?: FieldError; | ||||||||||||||||||||||||||||||||||||||||||||
| required?: boolean; | ||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+25
to
+31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major Remove duplicate type definition. The Apply this diff: import { cn } from '@/lib/utils';
import countryList from 'react-select-country-list';
-type CountrySelectProps = {
- name: string;
- label: string;
- control: Control<any>;
- error?: FieldError;
- required?: boolean;
-};
-
const CountrySelect = ({📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| const CountrySelect = ({ | ||||||||||||||||||||||||||||||||||||||||||||
| value, | ||||||||||||||||||||||||||||||||||||||||||||
| onChange, | ||||||||||||||||||||||||||||||||||||||||||||
| }: { | ||||||||||||||||||||||||||||||||||||||||||||
| value: string; | ||||||||||||||||||||||||||||||||||||||||||||
| onChange: (value: string) => void; | ||||||||||||||||||||||||||||||||||||||||||||
| }) => { | ||||||||||||||||||||||||||||||||||||||||||||
| const [open, setOpen] = useState(false); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| // Get country options with flags | ||||||||||||||||||||||||||||||||||||||||||||
| const countries = countryList().getData(); | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| // Helper function to get flag emoji | ||||||||||||||||||||||||||||||||||||||||||||
| const getFlagEmoji = (countryCode: string) => { | ||||||||||||||||||||||||||||||||||||||||||||
| const codePoints = countryCode | ||||||||||||||||||||||||||||||||||||||||||||
| .toUpperCase() | ||||||||||||||||||||||||||||||||||||||||||||
| .split('') | ||||||||||||||||||||||||||||||||||||||||||||
| .map((char) => 127397 + char.charCodeAt(0)); | ||||||||||||||||||||||||||||||||||||||||||||
| return String.fromCodePoint(...codePoints); | ||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||
| <Popover open={open} onOpenChange={setOpen}> | ||||||||||||||||||||||||||||||||||||||||||||
| <PopoverTrigger asChild> | ||||||||||||||||||||||||||||||||||||||||||||
| <Button | ||||||||||||||||||||||||||||||||||||||||||||
| variant='outline' | ||||||||||||||||||||||||||||||||||||||||||||
| role='combobox' | ||||||||||||||||||||||||||||||||||||||||||||
| aria-expanded={open} | ||||||||||||||||||||||||||||||||||||||||||||
| className='country-select-trigger' | ||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||
| {value ? ( | ||||||||||||||||||||||||||||||||||||||||||||
| <span className='flex items-center gap-2'> | ||||||||||||||||||||||||||||||||||||||||||||
| <span>{getFlagEmoji(value)}</span> | ||||||||||||||||||||||||||||||||||||||||||||
| <span>{countries.find((c) => c.value === value)?.label}</span> | ||||||||||||||||||||||||||||||||||||||||||||
| </span> | ||||||||||||||||||||||||||||||||||||||||||||
| ) : ( | ||||||||||||||||||||||||||||||||||||||||||||
| 'Select your country...' | ||||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||||
| <ChevronsUpDown className='ml-2 h-4 w-4 shrink-0 opacity-50' /> | ||||||||||||||||||||||||||||||||||||||||||||
| </Button> | ||||||||||||||||||||||||||||||||||||||||||||
| </PopoverTrigger> | ||||||||||||||||||||||||||||||||||||||||||||
| <PopoverContent | ||||||||||||||||||||||||||||||||||||||||||||
| className='w-full p-0 bg-gray-800 border-gray-600' | ||||||||||||||||||||||||||||||||||||||||||||
| align='start' | ||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||
| <Command className='bg-gray-800 border-gray-600'> | ||||||||||||||||||||||||||||||||||||||||||||
| <CommandInput | ||||||||||||||||||||||||||||||||||||||||||||
| placeholder='Search countries...' | ||||||||||||||||||||||||||||||||||||||||||||
| className='country-select-input' | ||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||
| <CommandEmpty className='country-select-empty'> | ||||||||||||||||||||||||||||||||||||||||||||
| No country found. | ||||||||||||||||||||||||||||||||||||||||||||
| </CommandEmpty> | ||||||||||||||||||||||||||||||||||||||||||||
| <CommandList className='max-h-60 bg-gray-800 scrollbar-hide-default'> | ||||||||||||||||||||||||||||||||||||||||||||
| <CommandGroup className='bg-gray-800'> | ||||||||||||||||||||||||||||||||||||||||||||
| {countries.map((country) => ( | ||||||||||||||||||||||||||||||||||||||||||||
| <CommandItem | ||||||||||||||||||||||||||||||||||||||||||||
| key={country.value} | ||||||||||||||||||||||||||||||||||||||||||||
| value={`${country.label} ${country.value}`} | ||||||||||||||||||||||||||||||||||||||||||||
| onSelect={() => { | ||||||||||||||||||||||||||||||||||||||||||||
| onChange(country.value); | ||||||||||||||||||||||||||||||||||||||||||||
| setOpen(false); | ||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||
| className='country-select-item' | ||||||||||||||||||||||||||||||||||||||||||||
| > | ||||||||||||||||||||||||||||||||||||||||||||
| <Check | ||||||||||||||||||||||||||||||||||||||||||||
| className={cn( | ||||||||||||||||||||||||||||||||||||||||||||
| 'mr-2 h-4 w-4 text-yellow-500', | ||||||||||||||||||||||||||||||||||||||||||||
| value === country.value ? 'opacity-100' : 'opacity-0' | ||||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||
| <span className='flex items-center gap-2'> | ||||||||||||||||||||||||||||||||||||||||||||
| <span>{getFlagEmoji(country.value)}</span> | ||||||||||||||||||||||||||||||||||||||||||||
| <span>{country.label}</span> | ||||||||||||||||||||||||||||||||||||||||||||
| </span> | ||||||||||||||||||||||||||||||||||||||||||||
| </CommandItem> | ||||||||||||||||||||||||||||||||||||||||||||
| ))} | ||||||||||||||||||||||||||||||||||||||||||||
| </CommandGroup> | ||||||||||||||||||||||||||||||||||||||||||||
| </CommandList> | ||||||||||||||||||||||||||||||||||||||||||||
| </Command> | ||||||||||||||||||||||||||||||||||||||||||||
| </PopoverContent> | ||||||||||||||||||||||||||||||||||||||||||||
| </Popover> | ||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||
| export const CountrySelectField = ({ | ||||||||||||||||||||||||||||||||||||||||||||
| name, | ||||||||||||||||||||||||||||||||||||||||||||
| label, | ||||||||||||||||||||||||||||||||||||||||||||
| control, | ||||||||||||||||||||||||||||||||||||||||||||
| error, | ||||||||||||||||||||||||||||||||||||||||||||
| required = false, | ||||||||||||||||||||||||||||||||||||||||||||
| }: CountrySelectProps) => { | ||||||||||||||||||||||||||||||||||||||||||||
| return ( | ||||||||||||||||||||||||||||||||||||||||||||
| <div className='space-y-2'> | ||||||||||||||||||||||||||||||||||||||||||||
| <Label htmlFor={name} className='form-label'> | ||||||||||||||||||||||||||||||||||||||||||||
| {label} | ||||||||||||||||||||||||||||||||||||||||||||
| </Label> | ||||||||||||||||||||||||||||||||||||||||||||
| <Controller | ||||||||||||||||||||||||||||||||||||||||||||
| name={name} | ||||||||||||||||||||||||||||||||||||||||||||
| control={control} | ||||||||||||||||||||||||||||||||||||||||||||
| rules={{ | ||||||||||||||||||||||||||||||||||||||||||||
| required: required ? `Please select ${label.toLowerCase()}` : false, | ||||||||||||||||||||||||||||||||||||||||||||
| }} | ||||||||||||||||||||||||||||||||||||||||||||
| render={({ field }) => ( | ||||||||||||||||||||||||||||||||||||||||||||
| <CountrySelect value={field.value} onChange={field.onChange} /> | ||||||||||||||||||||||||||||||||||||||||||||
| )} | ||||||||||||||||||||||||||||||||||||||||||||
| /> | ||||||||||||||||||||||||||||||||||||||||||||
| {error && <p className='text-sm text-red-500'>{error.message}</p>} | ||||||||||||||||||||||||||||||||||||||||||||
| <p className='text-xs text-gray-500'> | ||||||||||||||||||||||||||||||||||||||||||||
| Helps us show market data and news relevant to you. | ||||||||||||||||||||||||||||||||||||||||||||
| </p> | ||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
|
|
||
| import Link from 'next/link' | ||
| import React from 'react' | ||
|
|
||
| const FooterLink = ({text, linkText , href} : FooterLinkProps) => ( | ||
| <div className='text-center pt-4'> | ||
| <p className='text-sm text-gray-500'> | ||
| {text}{` `} | ||
| <Link href={href} className='footer-link'> | ||
| {linkText} | ||
| </Link> | ||
| </p> | ||
| </div> | ||
| ) | ||
|
|
||
| export default FooterLink |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Remove duplicate type definition.
The
SignInFormDatatype is already defined globally intypes/global.d.ts(lines 2-5). This local redefinition is unnecessary and creates maintenance overhead.Apply this diff to remove the duplicate type:
'use client' import React from 'react' import InputField from '@/components/forms/InputField' import FooterLink from '@/components/forms/FooterLink' import { Button } from '@/components/ui/button' import { SubmitHandler, useForm } from 'react-hook-form' -type SignInFormData = { - email: string - password: string -} - const SignIn = () => {📝 Committable suggestion
🤖 Prompt for AI Agents