11import React , { useState , useEffect } from "react" ;
2- import { Button } from "@mui/material" ;
2+ import {
3+ Button ,
4+ Card ,
5+ CardContent ,
6+ TextField ,
7+ Typography ,
8+ Box ,
9+ Chip ,
10+ Tab ,
11+ Tabs ,
12+ Paper ,
13+ Stack ,
14+ } from "@mui/material" ;
315import { Grid } from "@mui/system" ;
4- import { useForm , useFormState } from "react-hook-form" ;
16+ import { useForm , useFormState , useWatch } from "react-hook-form" ;
517import { Add } from "@mui/icons-material" ;
618import { CippOffCanvas } from "./CippOffCanvas" ;
719import CippFormComponent from "./CippFormComponent" ;
@@ -10,6 +22,8 @@ import { ApiPostCall, ApiGetCall } from "../../api/ApiCall";
1022
1123export const CippAddTestReportDrawer = ( { buttonText = "Create custom report" } ) => {
1224 const [ drawerVisible , setDrawerVisible ] = useState ( false ) ;
25+ const [ activeTab , setActiveTab ] = useState ( 0 ) ;
26+ const [ searchTerm , setSearchTerm ] = useState ( "" ) ;
1327
1428 const formControl = useForm ( {
1529 mode : "onChange" ,
@@ -22,6 +36,10 @@ export const CippAddTestReportDrawer = ({ buttonText = "Create custom report" })
2236 } ) ;
2337
2438 const { isValid } = useFormState ( { control : formControl . control } ) ;
39+ const selectedIdentityTests =
40+ useWatch ( { control : formControl . control , name : "IdentityTests" } ) || [ ] ;
41+ const selectedDeviceTests =
42+ useWatch ( { control : formControl . control , name : "DevicesTests" } ) || [ ] ;
2543
2644 const createReport = ApiPostCall ( {
2745 urlFromData : true ,
@@ -69,6 +87,8 @@ export const CippAddTestReportDrawer = ({ buttonText = "Create custom report" })
6987
7088 const handleCloseDrawer = ( ) => {
7189 setDrawerVisible ( false ) ;
90+ setSearchTerm ( "" ) ;
91+ setActiveTab ( 0 ) ;
7292 formControl . reset ( {
7393 name : "" ,
7494 description : "" ,
@@ -77,6 +97,43 @@ export const CippAddTestReportDrawer = ({ buttonText = "Create custom report" })
7797 } ) ;
7898 } ;
7999
100+ const toggleTest = ( testId , testType ) => {
101+ const fieldName = testType === "Identity" ? "IdentityTests" : "DevicesTests" ;
102+ const currentTests = formControl . getValues ( fieldName ) || [ ] ;
103+
104+ if ( currentTests . includes ( testId ) ) {
105+ formControl . setValue (
106+ fieldName ,
107+ currentTests . filter ( ( id ) => id !== testId ) ,
108+ { shouldValidate : true }
109+ ) ;
110+ } else {
111+ formControl . setValue ( fieldName , [ ...currentTests , testId ] , { shouldValidate : true } ) ;
112+ }
113+ } ;
114+
115+ const isTestSelected = ( testId , testType ) => {
116+ return testType === "Identity"
117+ ? selectedIdentityTests . includes ( testId )
118+ : selectedDeviceTests . includes ( testId ) ;
119+ } ;
120+
121+ const filterTests = ( tests ) => {
122+ if ( ! searchTerm ) return tests ;
123+ return tests . filter (
124+ ( test ) =>
125+ test . id . toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) ) ||
126+ test . name . toLowerCase ( ) . includes ( searchTerm . toLowerCase ( ) )
127+ ) ;
128+ } ;
129+
130+ const currentTests =
131+ activeTab === 0
132+ ? filterTests ( availableTests . IdentityTests || [ ] )
133+ : filterTests ( availableTests . DevicesTests || [ ] ) ;
134+
135+ const currentTestType = activeTab === 0 ? "Identity" : "Devices" ;
136+
80137 return (
81138 < >
82139 < Button
@@ -116,53 +173,203 @@ export const CippAddTestReportDrawer = ({ buttonText = "Create custom report" })
116173 </ div >
117174 }
118175 >
119- < Grid container spacing = { 2 } >
120- < Grid size = { 12 } >
121- < CippFormComponent
122- type = "textField"
123- label = "Report Name"
124- name = "name"
125- formControl = { formControl }
126- validators = { { required : "Report Name is required" } }
127- />
128- </ Grid >
176+ < Grid container spacing = { 3 } >
177+ { /* Report Details Section */ }
129178 < Grid size = { 12 } >
130- < CippFormComponent
131- type = "textField"
132- label = "Description"
133- name = "description"
134- formControl = { formControl }
135- multiline
136- rows = { 3 }
137- />
179+ < Paper sx = { { p : 3 , backgroundColor : "background.default" } } >
180+ < Typography variant = "h6" gutterBottom >
181+ Report Details
182+ </ Typography >
183+ < Grid container spacing = { 2 } >
184+ < Grid size = { 12 } >
185+ < CippFormComponent
186+ type = "textField"
187+ label = "Report Name"
188+ name = "name"
189+ formControl = { formControl }
190+ validators = { { required : "Report Name is required" } }
191+ />
192+ </ Grid >
193+ < Grid size = { 12 } >
194+ < CippFormComponent
195+ type = "textField"
196+ label = "Description"
197+ name = "description"
198+ formControl = { formControl }
199+ multiline
200+ rows = { 3 }
201+ />
202+ </ Grid >
203+ </ Grid >
204+ </ Paper >
138205 </ Grid >
206+
207+ { /* Selection Summary */ }
139208 < Grid size = { 12 } >
140- < CippFormComponent
141- type = "autoComplete"
142- label = "Identity Tests"
143- name = "IdentityTests"
144- formControl = { formControl }
145- multiple
146- options = { availableTests . IdentityTests ?. map ( ( test ) => ( {
147- value : test . id ,
148- label : `${ test . id } - ${ test . name } ` ,
149- } ) ) }
150- isFetching = { availableTestsApi . isFetching }
151- />
209+ < Paper sx = { { p : 2 , backgroundColor : "primary.50" } } >
210+ < Stack direction = "row" spacing = { 2 } alignItems = "center" >
211+ < Typography variant = "subtitle2" color = "primary" >
212+ Selected Tests:
213+ </ Typography >
214+ < Chip
215+ label = { `${ selectedIdentityTests . length } Identity` }
216+ color = "primary"
217+ size = "small"
218+ variant = "outlined"
219+ />
220+ < Chip
221+ label = { `${ selectedDeviceTests . length } Device` }
222+ color = "secondary"
223+ size = "small"
224+ variant = "outlined"
225+ />
226+ < Box sx = { { flex : 1 } } />
227+ < Typography variant = "caption" color = "text.secondary" >
228+ Total: { selectedIdentityTests . length + selectedDeviceTests . length } tests
229+ </ Typography >
230+ </ Stack >
231+ </ Paper >
152232 </ Grid >
233+
234+ { /* Test Selection Section */ }
153235 < Grid size = { 12 } >
154- < CippFormComponent
155- type = "autoComplete"
156- label = "Device Tests"
157- name = "DevicesTests"
158- formControl = { formControl }
159- multiple
160- options = { availableTests . DevicesTests ?. map ( ( test ) => ( {
161- value : test . id ,
162- label : `${ test . id } - ${ test . name } ` ,
163- } ) ) }
164- isFetching = { availableTestsApi . isFetching }
165- />
236+ < Paper sx = { { p : 0 } } >
237+ < Box sx = { { borderBottom : 1 , borderColor : "divider" } } >
238+ < Tabs
239+ value = { activeTab }
240+ onChange = { ( e , newValue ) => {
241+ setActiveTab ( newValue ) ;
242+ setSearchTerm ( "" ) ;
243+ } }
244+ variant = "fullWidth"
245+ >
246+ < Tab
247+ label = {
248+ < Box sx = { { display : "flex" , alignItems : "center" , gap : 1 } } >
249+ < span > Identity Tests</ span >
250+ { selectedIdentityTests . length > 0 && (
251+ < Chip size = "small" label = { selectedIdentityTests . length } color = "primary" />
252+ ) }
253+ </ Box >
254+ }
255+ />
256+ < Tab
257+ label = {
258+ < Box sx = { { display : "flex" , alignItems : "center" , gap : 1 } } >
259+ < span > Device Tests</ span >
260+ { selectedDeviceTests . length > 0 && (
261+ < Chip size = "small" label = { selectedDeviceTests . length } color = "secondary" />
262+ ) }
263+ </ Box >
264+ }
265+ />
266+ </ Tabs >
267+ </ Box >
268+
269+ { /* Search Bar */ }
270+ < Box sx = { { p : 2 , borderBottom : 1 , borderColor : "divider" } } >
271+ < TextField
272+ fullWidth
273+ size = "small"
274+ placeholder = { `Search ${ currentTestType } tests...` }
275+ value = { searchTerm }
276+ onChange = { ( e ) => setSearchTerm ( e . target . value ) }
277+ />
278+ </ Box >
279+
280+ { /* Test List */ }
281+ < Box
282+ sx = { {
283+ maxHeight : "400px" ,
284+ overflowY : "auto" ,
285+ p : 2 ,
286+ } }
287+ >
288+ { availableTestsApi . isFetching ? (
289+ < Box sx = { { textAlign : "center" , py : 4 } } >
290+ < Typography color = "text.secondary" > Loading tests...</ Typography >
291+ </ Box >
292+ ) : currentTests . length === 0 ? (
293+ < Box sx = { { textAlign : "center" , py : 4 } } >
294+ < Typography color = "text.secondary" >
295+ { searchTerm ? "No tests found matching your search" : "No tests available" }
296+ </ Typography >
297+ </ Box >
298+ ) : (
299+ < Grid container spacing = { 1 } >
300+ { currentTests . map ( ( test ) => {
301+ const isSelected = isTestSelected ( test . id , currentTestType ) ;
302+ return (
303+ < Grid size = { 12 } key = { test . id } >
304+ < Card
305+ sx = { {
306+ cursor : "pointer" ,
307+ transition : "all 0.2s" ,
308+ border : 1 ,
309+ borderColor : isSelected ? "primary.main" : "divider" ,
310+ backgroundColor : isSelected ? "primary.50" : "background.paper" ,
311+ "&:hover" : {
312+ borderColor : "primary.main" ,
313+ boxShadow : 2 ,
314+ } ,
315+ } }
316+ onClick = { ( ) => toggleTest ( test . id , currentTestType ) }
317+ >
318+ < CardContent sx = { { p : 2 , "&:last-child" : { pb : 2 } } } >
319+ < Box sx = { { display : "flex" , alignItems : "center" , gap : 1.5 } } >
320+ < Box sx = { { flex : 1 , minWidth : 0 } } >
321+ < Box
322+ sx = { {
323+ display : "flex" ,
324+ alignItems : "center" ,
325+ gap : 1 ,
326+ mb : 0.5 ,
327+ } }
328+ >
329+ < Chip
330+ label = { test . id }
331+ size = "small"
332+ color = { isSelected ? "primary" : "default" }
333+ sx = { { fontFamily : "monospace" , fontSize : "0.75rem" } }
334+ />
335+ < Typography
336+ variant = "body2"
337+ sx = { {
338+ fontWeight : isSelected ? 600 : 400 ,
339+ color : isSelected ? "primary.main" : "text.primary" ,
340+ overflow : "hidden" ,
341+ textOverflow : "ellipsis" ,
342+ whiteSpace : "nowrap" ,
343+ } }
344+ >
345+ { test . name }
346+ </ Typography >
347+ </ Box >
348+ { test . description && (
349+ < Typography
350+ variant = "caption"
351+ color = "text.secondary"
352+ sx = { {
353+ display : "-webkit-box" ,
354+ WebkitLineClamp : 2 ,
355+ WebkitBoxOrient : "vertical" ,
356+ overflow : "hidden" ,
357+ } }
358+ >
359+ { test . description }
360+ </ Typography >
361+ ) }
362+ </ Box >
363+ </ Box >
364+ </ CardContent >
365+ </ Card >
366+ </ Grid >
367+ ) ;
368+ } ) }
369+ </ Grid >
370+ ) }
371+ </ Box >
372+ </ Paper >
166373 </ Grid >
167374 </ Grid >
168375 </ CippOffCanvas >
0 commit comments