Skip to content

Commit

Permalink
feat: update signin, signup, otp and new password pages
Browse files Browse the repository at this point in the history
  • Loading branch information
americano98 committed Nov 28, 2024
1 parent edd3e52 commit 3e06930
Show file tree
Hide file tree
Showing 22 changed files with 11,352 additions and 14,582 deletions.
39 changes: 33 additions & 6 deletions apps/gitness/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import { NuqsAdapter } from 'nuqs/adapters/react-router'

import { TooltipProvider } from '@harnessio/canary'
import { CodeServiceAPIClient } from '@harnessio/code-service-client'
import { EmptyPage } from '@harnessio/ui/views'
import {
EmptyPage,
ForgotPasswordPage as ForgotPasswordPageV2,
NewPasswordPage as NewPasswordPageV2,
OTPPage as OTPPageV2
} from '@harnessio/ui/views'
import {
ForgotPasswordPage,
NewPasswordPage,
Expand All @@ -33,6 +38,8 @@ import RepoLayoutV1 from './layouts/RepoLayout'
import RepoLayout from './pages-v2/repo/repo-layout'
import ReposListPage from './pages-v2/repo/repo-list'
import { RepoSidebar } from './pages-v2/repo/repo-sidebar'
import { SignIn as SignInV2 } from './pages-v2/signin'
import { SignUp as SignUpV2 } from './pages-v2/signup'
import CreateProject from './pages/create-project'
import { Execution } from './pages/execution/execution-details'
import RepoExecutionListPage from './pages/execution/repo-execution-list'
Expand Down Expand Up @@ -88,25 +95,45 @@ export default function App() {

const router = createBrowserRouter([
{
path: '/signin',
path: '/v1/signin',
element: <SignIn />
},
{
path: '/signup',
path: '/v1/signup',
element: <SignUp />
},
{
path: '/forgot',
path: '/v1/forgot',
element: <ForgotPasswordPage />
},
{
path: '/otp',
path: '/v1/otp',
element: <OTPPage />
},
{
path: '/new-password',
path: '/v1/new-password',
element: <NewPasswordPage />
},
{
path: '/signin',
element: <SignInV2 />
},
{
path: '/signup',
element: <SignUpV2 />
},
{
path: '/forgot',
element: <ForgotPasswordPageV2 />
},
{
path: '/otp',
element: <OTPPageV2 />
},
{
path: '/new-password',
element: <NewPasswordPageV2 />
},
{
path: '/',
element: <RootWrapper />,
Expand Down
45 changes: 45 additions & 0 deletions apps/gitness/src/pages-v2/signin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useNavigate } from 'react-router-dom'

import { getUser, useOnLoginMutation } from '@harnessio/code-service-client'
import { SignInDataProps, SignInPage } from '@harnessio/ui/views'

import { useAppContext } from '../framework/context/AppContext'

export const SignIn: React.FC = () => {
const navigate = useNavigate()
const { setCurrentUser } = useAppContext()
const {
mutate: login,
isLoading,
error
} = useOnLoginMutation(
{ queryParams: { include_cookie: true } },
{
onSuccess: () => {
getUser({})
.then(response => {
setCurrentUser(response.body)
})
.catch(_e => {
// Ignore/toast error
})
navigate('/') // Redirect to Home page
}
}
)

return (
<SignInPage
isLoading={isLoading}
handleSignIn={(data: SignInDataProps) => {
login({
body: {
login_identifier: data.email,
password: data.password
}
})
}}
error={error?.message}
/>
)
}
35 changes: 35 additions & 0 deletions apps/gitness/src/pages-v2/signup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { useEffect } from 'react'
import { useNavigate } from 'react-router-dom'

import { useOnRegisterMutation } from '@harnessio/code-service-client'
import { SignUpDataProps, SignUpPage } from '@harnessio/ui/views'

export const SignUp: React.FC = () => {
const navigate = useNavigate()

const {
mutate: register,
isLoading,
isSuccess,
error
} = useOnRegisterMutation({ queryParams: { include_cookie: true } })

useEffect(() => {
if (isSuccess) {
navigate('/') // Redirect to Home page
}
}, [isSuccess])

const handleSignUp = (data: SignUpDataProps) => {
register({
body: {
email: data.email,
password: data.password,
uid: data.userId,
display_name: data.userId
}
})
}

return <SignUpPage isLoading={isLoading} handleSignUp={handleSignUp} error={error?.message} />
}
1 change: 1 addition & 0 deletions packages/ui/src/components/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const buttonVariants = cva(
default: 'h-8 px-6 py-2',
sm: 'h-7 px-3 py-0 text-sm font-normal',
xs: 'h-auto px-1.5 py-0.5 text-xs font-normal',
md: 'h-9 px-7',
lg: 'h-10 px-8',
icon: 'size-8',
sm_icon: 'size-7',
Expand Down
82 changes: 82 additions & 0 deletions packages/ui/src/components/card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import * as React from 'react'

import { cn } from '@utils/cn'
import { cva, type VariantProps } from 'class-variance-authority'

const cardVariants = cva('bg-card text-card-foreground rounded-lg border shadow', {
variants: {
variant: {
default: '',
plain: 'border-none bg-transparent shadow-none'
}
},
defaultVariants: {
variant: 'default'
}
})

export interface CardProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof cardVariants> {
width?: 'auto' | 'full' | 'screen' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | string
}

const widthClasses = {
auto: 'w-auto',
full: 'w-full',
screen: 'w-screen',
fit: 'w-fit',
xs: 'w-24',
sm: 'w-48',
md: 'w-64',
lg: 'w-72',
xl: 'w-80',
'2xl': 'w-96'
}

const Card = React.forwardRef<HTMLDivElement, CardProps>(({ variant, className, width = 'auto', ...props }, ref) => {
const widthClass = widthClasses[width as keyof typeof widthClasses] || width

return <div ref={ref} className={cn(cardVariants({ variant }), widthClass, className)} {...props} />
})

Card.displayName = 'Card'

const CardHeader = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className = 'space-y-1.5 p-6', ...props }, ref) => (
<div ref={ref} className={cn('flex flex-col', className)} {...props} />
)
)
CardHeader.displayName = 'CardHeader'

interface CardTitleProps extends React.HTMLAttributes<HTMLHeadingElement> {
as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'
}

const CardTitle = React.forwardRef<HTMLHeadingElement, CardTitleProps>(
({ className, children, as: Tag = 'h3', ...props }, ref) => (
<Tag ref={ref} className={cn('font-medium leading-snug tracking-tight', className)} {...props}>
{children}
</Tag>
)
)
CardTitle.displayName = 'CardTitle'

const CardDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
({ className, ...props }, ref) => (
<p ref={ref} className={cn('text-muted-foreground text-sm', className)} {...props} />
)
)
CardDescription.displayName = 'CardDescription'

const CardContent = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className = 'p-6', ...props }, ref) => <div ref={ref} className={cn('pt-0', className)} {...props} />
)
CardContent.displayName = 'CardContent'

const CardFooter = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
({ className = 'p-6', ...props }, ref) => (
<div ref={ref} className={cn('flex items-center pt-0', className)} {...props} />
)
)
CardFooter.displayName = 'CardFooter'

export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent, cardVariants }
3 changes: 3 additions & 0 deletions packages/ui/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ export * from './tabs'
export * from './branch-selector'
export * from './command'
export * from './search-files'
export * from './card'
export * from './input'
export * from './input-otp'
export * as ListActions from './list-actions'
export * as ListPagination from './list-pagination'
export * as SearchBox from './search-box'
Expand Down
69 changes: 69 additions & 0 deletions packages/ui/src/components/input-otp.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import * as React from 'react'

import { DashIcon } from '@radix-ui/react-icons'
import { OTPInput, OTPInputContext } from 'input-otp'

import { cn } from '../utils/cn'

const InputOTP = React.forwardRef<React.ElementRef<typeof OTPInput>, React.ComponentPropsWithoutRef<typeof OTPInput>>(
({ className, containerClassName, ...props }, ref) => (
<OTPInput
ref={ref}
containerClassName={cn('flex items-center gap-3 has-[:disabled]:opacity-50', containerClassName)}
className={cn('disabled:cursor-not-allowed', className)}
{...props}
/>
)
)
InputOTP.displayName = 'InputOTP'

const InputOTPGroup = React.forwardRef<React.ElementRef<'div'>, React.ComponentPropsWithoutRef<'div'>>(
({ className, ...props }, ref) => <div ref={ref} className={cn('flex items-center', className)} {...props} />
)
InputOTPGroup.displayName = 'InputOTPGroup'

interface InputOTPSlotProps extends React.ComponentPropsWithoutRef<'div'> {
index: number
}

const InputOTPSlot = React.forwardRef<HTMLDivElement, InputOTPSlotProps>(({ index, className, ...props }, ref) => {
const inputOTPContext = React.useContext(OTPInputContext)

if (!inputOTPContext?.slots?.[index]) {
console.error(`No slot data available for index ${index}`)
return null
}

const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index]

return (
<div
ref={ref}
className={cn(
'relative flex items-center justify-center h-[52px] w-11 rounded border border-borders-1 p-2 text-xl transition-all',
isActive && 'border-borders-3 z-10',
className
)}
{...props}
>
{char}
{hasFakeCaret && (
<div className="pointer-events-none absolute inset-0 flex items-center justify-center">
<div className="animate-caret-blink bg-foreground h-4 w-px duration-1000" />
</div>
)}
</div>
)
})
InputOTPSlot.displayName = 'InputOTPSlot'

const InputOTPSeparator = React.forwardRef<React.ElementRef<'div'>, React.ComponentPropsWithoutRef<'div'>>(
({ ...props }, ref) => (
<div ref={ref} role="separator" {...props}>
<DashIcon />
</div>
)
)
InputOTPSeparator.displayName = 'InputOTPSeparator'

export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator }
35 changes: 28 additions & 7 deletions packages/ui/src/components/input.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,40 @@
import * as React from 'react'

import { cn } from '../utils/cn'
import { Text } from './'

export interface BaseInputProps extends React.InputHTMLAttributes<HTMLInputElement> {
isExtended?: boolean
error?: string
wrapperClassName?: string
}

const BaseInput = React.forwardRef<HTMLInputElement, InputProps>(({ className, type, isExtended, ...props }, ref) => {
const commonClassName = 'bg-transparent text-foreground-1 px-2.5 py-1'
const specificClassNames = isExtended
? 'border-none grow focus-visible:outline-none'
: 'flex h-8 w-full rounded border border-border-2 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-foreground-4 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50'
const BaseInput = React.forwardRef<HTMLInputElement, BaseInputProps>(
({ className, type, isExtended, error, wrapperClassName, ...props }, ref) => {
const commonClassName = 'bg-transparent placeholder:text-foreground-5 text-foreground-1 px-3 py-1'
const specificClassNames = isExtended
? 'border-none grow focus-visible:outline-none'
: cn(
'flex h-9 w-full rounded border text-sm shadow-sm transition-colors file:border-0 focus-visible:outline-none file:bg-transparent file:text-sm file:font-medium placeholder:text-foreground-4 disabled:cursor-not-allowed disabled:opacity-50',
error ? 'border-borders-danger' : 'border-borders-2 focus-visible:border-borders-3'
)

return <input className={cn(commonClassName, specificClassNames, className)} type={type} ref={ref} {...props} />
})
return (
<div className={cn('relative flex flex-col', wrapperClassName)}>
<input className={cn(commonClassName, specificClassNames, className)} type={type} ref={ref} {...props} />
{error && (
<Text
className="text-foreground-danger absolute top-full leading-none translate-y-1 tracking-tight"
weight="light"
size={0}
>
{error}
</Text>
)}
</div>
)
}
)
BaseInput.displayName = 'BaseInput'

export interface ExtendedInputProps extends BaseInputProps {
Expand Down
Loading

0 comments on commit 3e06930

Please sign in to comment.