11"use client" ;
22import { approvalRequestSubmit } from "@/server-actions/approval-flow/approvalRequestSubmit" ;
3- import { Flex , Text , Button , Grid , Box , Card , Container , Link , Heading , Separator , Table , TextField , TextArea , Select , Badge } from "@radix-ui/themes" ;
3+ import {
4+ Flex ,
5+ Text ,
6+ Button ,
7+ Grid ,
8+ Box ,
9+ Card ,
10+ Container ,
11+ Link ,
12+ Heading ,
13+ Separator ,
14+ Table ,
15+ TextField ,
16+ TextArea ,
17+ Select ,
18+ Badge ,
19+ Checkbox ,
20+ } from "@radix-ui/themes" ;
421import { useFormState , useFormStatus } from "react-dom" ;
522import { ApprovalFlow } from "@/type" ;
623import { InputParamsFormInput } from "./inputParam" ;
@@ -21,8 +38,8 @@ const parseDuration = (duration: string): { days: number; hours: number } | null
2138} ;
2239
2340// Helper to format days and hours into ISO 8601 duration
24- const formatDuration = ( days : number , hours : number ) : string => {
25- if ( days === 0 && hours === 0 ) return "" ;
41+ const formatDuration = ( days : number , hours : number ) : string | undefined => {
42+ if ( days === 0 && hours === 0 ) return undefined ;
2643 if ( days === 0 ) return `PT${ hours } H` ;
2744 if ( hours === 0 ) return `P${ days } D` ;
2845 return `P${ days } DT${ hours } H` ;
@@ -31,7 +48,7 @@ const formatDuration = (days: number, hours: number): string => {
3148function AutoRevokeDurationInput ( {
3249 autoRevoke,
3350} : {
34- autoRevoke ? : {
51+ autoRevoke : {
3552 enabled : boolean ;
3653 required : boolean ;
3754 maxDuration ?: string ;
@@ -40,38 +57,36 @@ function AutoRevokeDurationInput({
4057 const [ days , setDays ] = useState < number > ( 0 ) ;
4158 const [ hours , setHours ] = useState < number > ( 0 ) ;
4259 const [ error , setError ] = useState < string | null > ( null ) ;
60+ const [ enableAutoRevoke , setEnableAutoRevoke ] = useState < boolean > ( autoRevoke . required ) ;
4361
4462 // Use useMemo to prevent recalculation on every render
45- const defaultMaxDuration = useMemo ( ( ) => {
46- return autoRevoke ?. maxDuration ? parseDuration ( autoRevoke . maxDuration ) : { days : 30 , hours : 23 } ;
63+ const maxDuration = useMemo ( ( ) => {
64+ return autoRevoke ?. maxDuration ? parseDuration ( autoRevoke . maxDuration ) : { days : 30 , hours : 24 } ;
4765 } , [ autoRevoke ?. maxDuration ] ) ;
4866
4967 // Validate and update the hidden input value when days or hours change
5068 useEffect ( ( ) => {
5169 const validateDuration = ( ) => {
70+ if ( ! enableAutoRevoke ) {
71+ setError ( null ) ;
72+ return true ;
73+ }
74+
5275 if ( days === 0 && hours === 0 ) {
5376 if ( autoRevoke ?. required ) {
5477 setError ( "Duration is required" ) ;
5578 return false ;
5679 }
5780 }
5881
59- if ( days > 30 ) {
60- setError ( "Days cannot exceed 30" ) ;
61- return false ;
62- }
63-
64- if ( hours > 24 ) {
65- setError ( "Hours cannot exceed 24" ) ;
66- return false ;
67- }
68-
69- if ( defaultMaxDuration ) {
70- const totalHours = days * 24 + hours ;
71- const maxTotalHours = defaultMaxDuration . days * 24 + defaultMaxDuration . hours ;
82+ if ( maxDuration ) {
83+ if ( days > maxDuration . days ) {
84+ setError ( `Days cannot exceed ${ maxDuration . days } ` ) ;
85+ return false ;
86+ }
7287
73- if ( totalHours > maxTotalHours ) {
74- setError ( `Duration cannot exceed ${ defaultMaxDuration . days } days and ${ defaultMaxDuration . hours } hours ` ) ;
88+ if ( hours > maxDuration . hours ) {
89+ setError ( `Hours cannot exceed ${ maxDuration . hours } ` ) ;
7590 return false ;
7691 }
7792 }
@@ -81,51 +96,72 @@ function AutoRevokeDurationInput({
8196 } ;
8297
8398 validateDuration ( ) ;
84- } , [ days , hours , autoRevoke , defaultMaxDuration ] ) ;
99+ } , [ days , hours , autoRevoke , maxDuration , enableAutoRevoke ] ) ;
85100
86101 if ( ! autoRevoke ?. enabled ) return null ;
87102
103+ // Only set the duration value if auto-revoke is enabled
104+ const durationValue = enableAutoRevoke ? formatDuration ( days , hours ) : undefined ;
105+
88106 return (
89107 < Flex direction = "column" gap = "2" >
90108 < Flex align = "center" gap = "2" >
91- < Heading size = "3" > Duration Until Auto Revoke</ Heading >
109+ < Heading size = "3" > Auto Revoke</ Heading >
92110 < Badge color = "amber" size = "1" >
93111 Preview
94112 </ Badge >
95113 </ Flex >
96- < Text size = "2" color = "gray" >
97- Specify how long this access should be granted (maximum: { defaultMaxDuration ?. days } days and { defaultMaxDuration ?. hours } hours)
98- </ Text >
99- < Flex gap = "2" align = "center" >
100- < TextField . Root
101- type = "number"
102- min = "0"
103- max = "30"
104- value = { days . toString ( ) }
105- onChange = { ( e ) => setDays ( parseInt ( e . target . value ) || 0 ) }
106- placeholder = "Days"
107- aria-label = "Days"
108- style = { { width : "30px" } }
109- />
110- < Text > days</ Text >
111- < TextField . Root
112- type = "number"
113- min = "0"
114- max = "23"
115- value = { hours . toString ( ) }
116- onChange = { ( e ) => setHours ( parseInt ( e . target . value ) || 0 ) }
117- placeholder = "Hours"
118- aria-label = "Hours"
119- style = { { width : "30px" } }
120- />
121- < Text > hours</ Text >
122- </ Flex >
123- { error && (
124- < Text size = "2" color = "red" >
125- { error }
126- </ Text >
114+
115+ { ! autoRevoke . required && (
116+ < Flex gap = "2" align = "center" >
117+ < Checkbox id = "enable-auto-revoke" checked = { enableAutoRevoke } onCheckedChange = { ( checked ) => setEnableAutoRevoke ( checked === true ) } />
118+ < Text as = "label" size = "2" htmlFor = "enable-auto-revoke" >
119+ Enable automatic revocation after specified duration
120+ </ Text >
121+ </ Flex >
122+ ) }
123+
124+ { ( autoRevoke . required || enableAutoRevoke ) && (
125+ < >
126+ < Text size = "2" color = "gray" >
127+ Specify how long this access should be granted (maximum: { maxDuration ?. days } days and { maxDuration ?. hours } hours)
128+ </ Text >
129+ < Flex gap = "2" align = "center" >
130+ < TextField . Root
131+ type = "number"
132+ min = "0"
133+ max = { maxDuration ?. days . toString ( ) }
134+ value = { days . toString ( ) }
135+ onChange = { ( e ) => setDays ( parseInt ( e . target . value ) || 0 ) }
136+ placeholder = "Days"
137+ aria-label = "Days"
138+ style = { { width : "80px" } }
139+ />
140+ < Text > days</ Text >
141+ < TextField . Root
142+ type = "number"
143+ min = "0"
144+ max = { maxDuration ?. hours . toString ( ) }
145+ value = { hours . toString ( ) }
146+ onChange = { ( e ) => setHours ( parseInt ( e . target . value ) || 0 ) }
147+ placeholder = "Hours"
148+ aria-label = "Hours"
149+ style = { { width : "80px" } }
150+ />
151+ < Text > hours</ Text >
152+ </ Flex >
153+ { error && (
154+ < Text size = "2" color = "red" >
155+ { error }
156+ </ Text >
157+ ) }
158+ </ >
159+ ) }
160+
161+ { /* Only include the hidden input when auto-revoke is enabled */ }
162+ { durationValue !== undefined && enableAutoRevoke && (
163+ < input type = "hidden" name = "autoRevokeDuration" value = { durationValue } required = { autoRevoke ?. required } />
127164 ) }
128- < input type = "hidden" name = "autoRevokeDuration" value = { formatDuration ( days , hours ) } required = { autoRevoke ?. required } />
129165 </ Flex >
130166 ) ;
131167}
0 commit comments