Skip to content

Commit a965954

Browse files
committed
Merge branch 'main' into feature/AYS-396/add-eslint-rules
2 parents 606ef9a + 99e1d01 commit a965954

File tree

11 files changed

+342
-11
lines changed

11 files changed

+342
-11
lines changed

src/app/(private)/admin-registration-applications/[id]/page.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
} from '@/components/ui/form'
1515
import { useForm } from 'react-hook-form'
1616
import { zodResolver } from '@hookform/resolvers/zod'
17-
import { FormSchema } from '@/modules/adminRegistrationApplications/constants/formValidationSchema'
17+
import { FormValidationSchema } from '@/modules/adminRegistrationApplications/constants/formValidationSchema'
1818
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
1919
import { useTranslation } from 'react-i18next'
2020
import { LoadingSpinner } from '@/components/ui/loadingSpinner'
@@ -35,7 +35,7 @@ const Page = ({ params }: { params: { slug: string; id: string } }) => {
3535
const { t } = useTranslation()
3636
const { toast } = useToast()
3737
const form = useForm({
38-
resolver: zodResolver(FormSchema),
38+
resolver: zodResolver(FormValidationSchema),
3939
})
4040
const { control } = form
4141

@@ -61,11 +61,13 @@ const Page = ({ params }: { params: { slug: string; id: string } }) => {
6161
.finally(() => setIsLoading(false))
6262
}
6363

64+
6465
fetchDetails()
6566
}, [params.id, t, toast])
6667

68+
6769
return (
68-
// <PrivateRoute requiredPermissions={[Permission.APPLICATION_DETAIL]}>
70+
<PrivateRoute requiredPermissions={[Permission.APPLICATION_DETAIL]}>
6971
<div className="p-6 bg-white dark:bg-gray-800 rounded-md shadow-md text-black dark:text-white">
7072
{isLoading && <LoadingSpinner />}
7173
{!isLoading && adminRegistrationApplicationDetails && (
@@ -170,7 +172,7 @@ const Page = ({ params }: { params: { slug: string; id: string } }) => {
170172
name="institutionName"
171173
render={({ field }) => (
172174
<FormItem className="sm:col-span-1">
173-
<FormLabel>{t('institution')}</FormLabel>
175+
<FormLabel>{t('institutionName')}</FormLabel>
174176
<FormControl>
175177
<Input
176178
{...field}
@@ -437,7 +439,7 @@ const Page = ({ params }: { params: { slug: string; id: string } }) => {
437439
</Form>
438440
)}
439441
</div>
440-
// </PrivateRoute>
442+
</PrivateRoute>
441443
)
442444
}
443445

src/app/(private)/admin-registration-applications/page.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import { useDataTable } from '@/app/hocs/useDataTable'
1414
import * as z from 'zod'
1515
import { DataTable, DataTableToolbar } from '@/components/dataTable'
1616
import filterFields from '@/modules/adminRegistrationApplications/constants/filterFields'
17+
import Link from 'next/link'
18+
import { Button } from '@/components/ui/button'
1719

1820
interface AdminRegistrationState {
1921
content: any[]
@@ -76,6 +78,12 @@ const Page = () => {
7678
<PrivateRoute requiredPermissions={[Permission.APPLICATION_LIST]}>
7779
<div className="space-y-1">
7880
{error && <Toaster />}
81+
<div className={'float-right'}>
82+
<Link href={'/admin-registration-applications/pre-application'}>
83+
<Button>{t('preApplication')}</Button>
84+
</Link>
85+
</div>
86+
7987
<DataTable
8088
className=""
8189
table={table}
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
'use client'
2+
3+
import {
4+
Form,
5+
FormControl,
6+
FormField,
7+
FormItem,
8+
FormLabel,
9+
FormMessage,
10+
} from '@/components/ui/form'
11+
import {
12+
Select,
13+
SelectContent,
14+
SelectItem,
15+
SelectTrigger,
16+
SelectValue,
17+
} from '@/components/ui/select'
18+
import { useForm } from 'react-hook-form'
19+
import { zodResolver } from '@hookform/resolvers/zod'
20+
import { z } from 'zod'
21+
import { Button } from '@/components/ui/button'
22+
import { useTranslation } from 'react-i18next'
23+
import Title from '@/components/ui/title'
24+
import { Textarea } from '@/components/ui/textarea'
25+
import { useEffect, useState } from 'react'
26+
import {
27+
approveAdminRegistrationApplication,
28+
getPreApplicationSummary,
29+
} from '@/modules/adminRegistrationApplications/service'
30+
import { useToast } from '@/components/ui/use-toast'
31+
import { LoadingSpinner } from '@/components/ui/loadingSpinner'
32+
import { Card } from '@/components/ui/card'
33+
import PrivateRoute from '@/app/hocs/isAuth'
34+
import { Permission } from '@/constants/permissions'
35+
import { useRouter } from 'next/navigation'
36+
import { PreApplicationFormSchema } from '@/modules/adminRegistrationApplications/constants/formValidationSchema'
37+
38+
const Page = () => {
39+
const { t } = useTranslation()
40+
const { toast } = useToast()
41+
const router = useRouter()
42+
const [isLoading, setIsLoading] = useState(true)
43+
const [institutionSummary, setInstitutionSummary] = useState<any>(null)
44+
45+
const form = useForm<z.infer<typeof PreApplicationFormSchema>>({
46+
resolver: zodResolver(PreApplicationFormSchema),
47+
defaultValues: {
48+
institutionId: '',
49+
reason: '',
50+
},
51+
})
52+
53+
function onSubmit(values: z.infer<typeof PreApplicationFormSchema>) {
54+
setIsLoading(true)
55+
approveAdminRegistrationApplication(values)
56+
.then(() => {
57+
toast({
58+
title: t('success'),
59+
description: t('preApplicationSuccess'),
60+
})
61+
router.push('/admin-registration-applications')
62+
})
63+
.catch(() => {
64+
toast({
65+
title: t('error'),
66+
description: t('preApplicationError'),
67+
variant: 'destructive',
68+
})
69+
})
70+
.finally(() => setIsLoading(false))
71+
}
72+
73+
useEffect(() => {
74+
getPreApplicationSummary()
75+
.then((response) => {
76+
setInstitutionSummary(response.data.response)
77+
})
78+
.catch(() => {
79+
toast({
80+
title: t('error'),
81+
description: t('defaultError'),
82+
variant: 'destructive',
83+
})
84+
})
85+
.finally(() => setIsLoading(false))
86+
}, [t, toast])
87+
88+
return (
89+
<PrivateRoute requiredPermissions={[Permission.APPLICATION_CREATE]}>
90+
<div className="p-6 bg-white dark:bg-gray-800 rounded-md shadow-md text-black dark:text-white">
91+
<Title title={t('preApplicationTitle')} />
92+
<Form {...form}>
93+
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
94+
<Card className="p-6 w-full">
95+
<div className="grid grid-cols-3 gap-y-6 sm:grid-cols-3 sm:gap-x-6">
96+
<FormField
97+
control={form.control}
98+
name="institutionId"
99+
render={({ field }) => (
100+
<>
101+
<FormItem className="col-span-1">
102+
<FormLabel>{t('institution')}</FormLabel>
103+
<FormControl>
104+
<Select
105+
onValueChange={field.onChange}
106+
defaultValue={field.value}
107+
>
108+
<SelectTrigger>
109+
<SelectValue
110+
placeholder={t('selectInstitution')}
111+
/>
112+
</SelectTrigger>
113+
<SelectContent>
114+
{institutionSummary?.map((item: any) => (
115+
<SelectItem key={item.id} value={item.id}>
116+
{item.name}
117+
</SelectItem>
118+
))}
119+
</SelectContent>
120+
</Select>
121+
</FormControl>
122+
<FormMessage />
123+
</FormItem>
124+
</>
125+
)}
126+
/>
127+
<FormField
128+
control={form.control}
129+
name="reason"
130+
render={({ field }) => (
131+
<>
132+
<FormItem className="col-span-2">
133+
<FormLabel>{t('createReason')}</FormLabel>
134+
<FormControl>
135+
<Textarea minLength={40} maxLength={512} {...field} />
136+
</FormControl>
137+
<FormMessage />
138+
</FormItem>
139+
</>
140+
)}
141+
/>
142+
</div>
143+
</Card>
144+
<Button disabled={isLoading} type="submit" className={'min-w-20'}>
145+
{isLoading ? <LoadingSpinner /> : t('create')}
146+
</Button>
147+
</form>
148+
</Form>
149+
</div>
150+
</PrivateRoute>
151+
)
152+
}
153+
154+
export default Page

src/components/ui/input.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import * as React from 'react'
2+
import * as React from 'react'
23

4+
import { cn } from '@/lib/utils'
35
import { cn } from '@/lib/utils'
46

57
export interface InputProps
@@ -18,8 +20,9 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
1820
{...props}
1921
/>
2022
)
21-
}
23+
},
2224
)
2325
Input.displayName = 'Input'
26+
Input.displayName = 'Input'
2427

2528
export { Input }

src/components/ui/select.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
'use client'
2+
'use client'
23

4+
import * as React from 'react'
5+
import * as SelectPrimitive from '@radix-ui/react-select'
6+
import { Check, ChevronDown, ChevronUp } from 'lucide-react'
37
import * as React from 'react'
48
import * as SelectPrimitive from '@radix-ui/react-select'
59
import { Check, ChevronDown, ChevronUp } from 'lucide-react'
610

11+
import { cn } from '@/lib/utils'
712
import { cn } from '@/lib/utils'
813

914
const Select = SelectPrimitive.Root
@@ -70,6 +75,7 @@ SelectScrollDownButton.displayName =
7075
const SelectContent = React.forwardRef<
7176
React.ElementRef<typeof SelectPrimitive.Content>,
7277
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
78+
>(({ className, children, position = 'popper', ...props }, ref) => (
7379
>(({ className, children, position = 'popper', ...props }, ref) => (
7480
<SelectPrimitive.Portal>
7581
<SelectPrimitive.Content
@@ -106,6 +112,7 @@ const SelectLabel = React.forwardRef<
106112
<SelectPrimitive.Label
107113
ref={ref}
108114
className={cn('py-1.5 pl-8 pr-2 text-sm font-semibold', className)}
115+
className={cn('py-1.5 pl-8 pr-2 text-sm font-semibold', className)}
109116
{...props}
110117
/>
111118
))
@@ -141,6 +148,7 @@ const SelectSeparator = React.forwardRef<
141148
<SelectPrimitive.Separator
142149
ref={ref}
143150
className={cn('-mx-1 my-1 h-px bg-muted', className)}
151+
className={cn('-mx-1 my-1 h-px bg-muted', className)}
144152
{...props}
145153
/>
146154
))

src/components/ui/textarea.tsx

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
'use client'
2+
import * as React from 'react'
3+
import { cn } from '@/lib/utils'
4+
import { useImperativeHandle } from 'react'
5+
6+
interface UseAutosizeTextAreaProps {
7+
textAreaRef: HTMLTextAreaElement | null
8+
minHeight?: number
9+
maxHeight?: number
10+
triggerAutoSize: string
11+
}
12+
13+
export const useAutosizeTextArea = ({
14+
textAreaRef,
15+
triggerAutoSize,
16+
maxHeight = Number.MAX_SAFE_INTEGER,
17+
minHeight = 0,
18+
}: UseAutosizeTextAreaProps) => {
19+
const [init, setInit] = React.useState(true)
20+
React.useEffect(() => {
21+
// We need to reset the height momentarily to get the correct scrollHeight for the textarea
22+
const offsetBorder = 2
23+
if (textAreaRef) {
24+
if (init) {
25+
textAreaRef.style.minHeight = `${minHeight + offsetBorder}px`
26+
if (maxHeight > minHeight) {
27+
textAreaRef.style.maxHeight = `${maxHeight}px`
28+
}
29+
setInit(false)
30+
}
31+
textAreaRef.style.height = `${minHeight + offsetBorder}px`
32+
const scrollHeight = textAreaRef.scrollHeight
33+
// We then set the height directly, outside of the render loop
34+
// Trying to set this with state or a ref will product an incorrect value.
35+
if (scrollHeight > maxHeight) {
36+
textAreaRef.style.height = `${maxHeight}px`
37+
} else {
38+
textAreaRef.style.height = `${scrollHeight + offsetBorder}px`
39+
}
40+
}
41+
}, [textAreaRef, triggerAutoSize, maxHeight, minHeight, init])
42+
}
43+
44+
export type AutosizeTextAreaRef = {
45+
textArea: HTMLTextAreaElement
46+
maxHeight: number
47+
minHeight: number
48+
}
49+
50+
type AutosizeTextAreaProps = {
51+
maxHeight?: number
52+
minHeight?: number
53+
} & React.TextareaHTMLAttributes<HTMLTextAreaElement>
54+
55+
export const AutosizeTextarea = React.forwardRef<
56+
AutosizeTextAreaRef,
57+
AutosizeTextAreaProps
58+
>(
59+
(
60+
{
61+
maxHeight = Number.MAX_SAFE_INTEGER,
62+
minHeight = 52,
63+
className,
64+
onChange,
65+
value,
66+
...props
67+
}: AutosizeTextAreaProps,
68+
ref: React.Ref<AutosizeTextAreaRef>,
69+
) => {
70+
const textAreaRef = React.useRef<HTMLTextAreaElement | null>(null)
71+
const [triggerAutoSize, setTriggerAutoSize] = React.useState('')
72+
73+
useAutosizeTextArea({
74+
textAreaRef: textAreaRef.current,
75+
triggerAutoSize: triggerAutoSize,
76+
maxHeight,
77+
minHeight,
78+
})
79+
80+
useImperativeHandle(ref, () => ({
81+
textArea: textAreaRef.current as HTMLTextAreaElement,
82+
focus: () => textAreaRef.current?.focus(),
83+
maxHeight,
84+
minHeight,
85+
}))
86+
87+
React.useEffect(() => {
88+
setTriggerAutoSize(value as string)
89+
}, [props?.defaultValue, value])
90+
91+
return (
92+
<textarea
93+
{...props}
94+
value={value}
95+
ref={textAreaRef}
96+
className={cn(
97+
'flex w-full rounded-md border border-input bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
98+
className,
99+
)}
100+
onChange={(e) => {
101+
setTriggerAutoSize(e.target.value)
102+
onChange?.(e)
103+
}}
104+
/>
105+
)
106+
},
107+
)
108+
AutosizeTextarea.displayName = 'AutosizeTextarea'
109+
110+
export { AutosizeTextarea as Textarea }

0 commit comments

Comments
 (0)