From de1cc4ccf77a19b7d9355cd18da27623d5d876b3 Mon Sep 17 00:00:00 2001 From: "[[alvarobernal2412]]" Date: Thu, 17 Apr 2025 20:14:40 +0200 Subject: [PATCH 01/24] feat(dashboard): added empty dashboard form --- src/forms/dashboard/form.jsx | 43 ++++++++++++++++++++++-------- src/forms/dashboard/schemas.js | 2 +- src/pages/app/Dashboards.jsx | 7 ++--- src/services/grafana/dashboards.js | 11 +++++++- 4 files changed, 47 insertions(+), 16 deletions(-) diff --git a/src/forms/dashboard/form.jsx b/src/forms/dashboard/form.jsx index b895a392..cfc7f633 100644 --- a/src/forms/dashboard/form.jsx +++ b/src/forms/dashboard/form.jsx @@ -1,5 +1,6 @@ import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; +import { useEffect, useState } from 'react'; import { Dialog, DialogContent, @@ -29,20 +30,40 @@ import { CalendarIcon } from 'lucide-react'; import { format } from 'date-fns'; import { cn } from '@/lib/utils'; import { dashboardFormSchema } from './schemas'; +import { foldersService } from '@/services/grafana/folders'; -// Mock folder data -const mockFolders = [ - { id: '1', name: 'Default' }, - { id: '2', name: 'Folder 1' }, - { id: '3', name: 'Folder 2' }, -]; +// Default folder option +const defaultFolder = { uid: null, title: 'Default' }; export function DashboardForm({ onClose, onSubmit }) { + const [folders, setFolders] = useState([defaultFolder]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const fetchFolders = async () => { + try { + const response = await foldersService.getAll(); + const fetchedFolders = response.data || response; + // Add the default folder and combine with fetched folders + setFolders([ + defaultFolder, + ...fetchedFolders.map(folder => ({ uid: folder.uid, title: folder.title })) + ]); + } catch (error) { + console.error('Error loading folders:', error); + } finally { + setLoading(false); + } + }; + + fetchFolders(); + }, []); + const form = useForm({ resolver: zodResolver(dashboardFormSchema), defaultValues: { name: '', - folderId: '1', + folderId: null, startDate: new Date(), endDate: undefined, }, @@ -79,16 +100,16 @@ export function DashboardForm({ onClose, onSubmit }) { render={({ field }) => ( Folder - - {mockFolders.map((f) => ( - - {f.name} + {folders.map((folder) => ( + + {folder.title} ))} diff --git a/src/forms/dashboard/schemas.js b/src/forms/dashboard/schemas.js index 8d28adc1..9c742e09 100644 --- a/src/forms/dashboard/schemas.js +++ b/src/forms/dashboard/schemas.js @@ -2,7 +2,7 @@ import * as z from 'zod'; export const dashboardFormSchema = z.object({ name: z.string().min(1, 'Dashboard name is required').max(100, 'Dashboard name must be 100 characters or less'), - folderId: z.string().min(1, 'Folder is required'), + folderId: z.string().nullable(), startDate: z.date({ required_error: 'Start date is required', invalid_type_error: "That's not a valid date!", diff --git a/src/pages/app/Dashboards.jsx b/src/pages/app/Dashboards.jsx index 1947dd89..68dc5e8e 100644 --- a/src/pages/app/Dashboards.jsx +++ b/src/pages/app/Dashboards.jsx @@ -65,9 +65,10 @@ export function Dashboards() { }; const handleDashboardFormSubmit = async (data) => { + console.log('DashboardForm data:', data); try { setLoading(true); - await dashboardsService.create(data); + await dashboardsService.createTemplate(data); // Recargar datos para mostrar el nuevo dashboard const searchResponse = await searchService.search({}); @@ -179,7 +180,7 @@ export function Dashboards() { disabled={loading || selectedItemsCount === 0} userRole={userData.authority} > - Delete + @@ -192,32 +187,43 @@ export function CatalogControlsStep({ initialControls = [], catalogId, onSubmit, )} - {/* Controls list */} -
- {controls.length === 0 ? ( -
-

No controls added yet. Click "Add Control" to start.

+ {/* Empty state with helpful message */} + {!isLoading && controls.length === 0 && ( +
+
+
- ) : ( - controls.map((control, index) => ( - +

No draft controls yet

+

Add draft controls to define what to monitor in your catalog

+
+ )} + + {/* Controls list with shimmer effect when loading */} +
+ {isLoading ? ( + // Shimmer loading effect for controls + Array(3).fill(0).map((_, index) => ( +
+
+
+ )) + ) : controls.length > 0 ? ( + controls.map((control) => ( + -
+
{control.name} - - {getSeverityBadge(control.severity)} -

- Type: {controlTypes.find(type => type.value === control.type)?.label || control.type} + Period: {control.period}

@@ -225,7 +231,7 @@ export function CatalogControlsStep({ initialControls = [], catalogId, onSubmit, size="sm" variant="ghost" className="text-red-500 hover:bg-red-50 hover:text-red-700" - onClick={() => handleDeleteControl(index)} + onClick={() => handleDeleteConfirm(control)} > @@ -233,159 +239,92 @@ export function CatalogControlsStep({ initialControls = [], catalogId, onSubmit,

{control.description}

- {control.implementation && ( -
- Implementation: -

{control.implementation}

+ + {/* Añadimos la visualización de scopes con badges */} + {control.scopes && Object.keys(control.scopes).length > 0 && ( +
+
+ {Object.entries(control.scopes).map(([key, value]) => ( + + {key}: {value} + + ))} +
)} + +
+
Start Date: {new Date(control.startDate).toLocaleDateString()}
+ {control.endDate &&
End Date: {new Date(control.endDate).toLocaleDateString()}
} + {control.mashupId &&
API Flow ID: {control.mashupId}
} + {control.params && Object.keys(control.params).length > 0 && ( +
+ Parameters: + {Object.entries(control.params).map(([key, value]) => ( + + {key}: {typeof value === 'object' ? JSON.stringify(value) : value} + + ))} +
+ )} +
)) - )} + ) : null}
- {/* Control form dialog */} - - + {/* Draft Control Form - Pass customSubmit for draft control creation */} + {openNewControlForm && ( + + )} + + {/* Delete Confirmation Dialog */} + + - {isEditing ? 'Edit Control' : 'Add New Control'} + Delete Draft Control - -
- - ( - - Control Name * - - - - - - )} - /> - -
- ( - - Type * - - - - )} - /> - - ( - - Severity * - - - - )} - /> -
- - ( - - Description * - -