Skip to main content
Glama

DHIS2 MCP Server

by Dradebo
sample-ui-components-output.md11.6 kB
# DHIS2 UI Components Generation *Generated by DHIS2 MCP Server - dhis2_create_ui_components tool* ## Component: DataElementForm *Form component with validation, date picker, and multi-select capabilities* ### Generated Files #### 1. DataElementForm.tsx ```typescript import React, { useState } from 'react' import { Button, Input, Field, Select, TextArea, Checkbox, DatePicker, MultiSelect, FormField, hasValue, composeValidators, } from '@dhis2/ui' import { useDataMutation } from '@dhis2/app-runtime' interface DataElementFormProps { onSubmit: (data: any) => void initialValues?: any loading?: boolean } const createMutation = { resource: 'dataElements', type: 'create', data: ({ data }: any) => data } export const DataElementForm: React.FC<DataElementFormProps> = ({ onSubmit, initialValues = {}, loading = false }) => { const [formData, setFormData] = useState({ name: initialValues.name || '', shortName: initialValues.shortName || '', description: initialValues.description || '', valueType: initialValues.valueType || 'TEXT', domainType: initialValues.domainType || 'AGGREGATE', aggregationType: initialValues.aggregationType || 'SUM', categories: initialValues.categories || [], active: initialValues.active ?? true, ...initialValues }) const [errors, setErrors] = useState<Record<string, string>>({}) const [mutate, { loading: mutationLoading }] = useDataMutation(createMutation) const validateField = (name: string, value: any) => { switch (name) { case 'name': return !value ? 'Name is required' : value.length < 3 ? 'Name must be at least 3 characters' : null case 'shortName': return !value ? 'Short name is required' : value.length > 50 ? 'Short name must be 50 characters or less' : null case 'valueType': return !value ? 'Value type is required' : null default: return null } } const handleFieldChange = (name: string, value: any) => { setFormData(prev => ({ ...prev, [name]: value })) // Clear error when user starts typing if (errors[name]) { setErrors(prev => ({ ...prev, [name]: '' })) } } const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() // Validate all fields const newErrors: Record<string, string> = {} Object.keys(formData).forEach(key => { const error = validateField(key, formData[key as keyof typeof formData]) if (error) newErrors[key] = error }) if (Object.keys(newErrors).length > 0) { setErrors(newErrors) return } try { const result = await mutate({ data: formData }) onSubmit(result) } catch (error) { console.error('Submission error:', error) } } const valueTypeOptions = [ { label: 'Text', value: 'TEXT' }, { label: 'Number', value: 'NUMBER' }, { label: 'Integer', value: 'INTEGER' }, { label: 'Boolean', value: 'BOOLEAN' }, { label: 'Date', value: 'DATE' }, { label: 'Percentage', value: 'PERCENTAGE' }, ] const aggregationTypeOptions = [ { label: 'Sum', value: 'SUM' }, { label: 'Average', value: 'AVERAGE' }, { label: 'Count', value: 'COUNT' }, { label: 'None', value: 'NONE' }, ] return ( <form onSubmit={handleSubmit} className="data-element-form"> <FormField label="Name" required error={!!errors.name} validationText={errors.name}> <Input value={formData.name} onChange={({ value }) => handleFieldChange('name', value)} placeholder="Enter data element name" error={!!errors.name} /> </FormField> <FormField label="Short Name" required error={!!errors.shortName} validationText={errors.shortName}> <Input value={formData.shortName} onChange={({ value }) => handleFieldChange('shortName', value)} placeholder="Enter short name (max 50 chars)" error={!!errors.shortName} /> </FormField> <FormField label="Description"> <TextArea value={formData.description} onChange={({ value }) => handleFieldChange('description', value)} placeholder="Enter description" rows={3} /> </FormField> <FormField label="Value Type" required error={!!errors.valueType} validationText={errors.valueType}> <Select selected={formData.valueType} onChange={({ selected }) => handleFieldChange('valueType', selected)} options={valueTypeOptions} placeholder="Select value type" /> </FormField> <FormField label="Aggregation Type"> <Select selected={formData.aggregationType} onChange={({ selected }) => handleFieldChange('aggregationType', selected)} options={aggregationTypeOptions} placeholder="Select aggregation type" /> </FormField> <FormField label="Categories"> <MultiSelect selected={formData.categories} onChange={({ selected }) => handleFieldChange('categories', selected)} options={[ { label: 'Age', value: 'age' }, { label: 'Gender', value: 'gender' }, { label: 'Location', value: 'location' }, ]} placeholder="Select categories" /> </FormField> <FormField> <Checkbox checked={formData.active} onChange={({ checked }) => handleFieldChange('active', checked)} label="Active" /> </FormField> <div className="form-actions"> <Button type="submit" primary loading={loading || mutationLoading} disabled={Object.keys(errors).length > 0} > Save Data Element </Button> <Button type="button" secondary> Cancel </Button> </div> </form> ) } ``` #### 2. DataElementForm.module.css ```css .data-element-form { max-width: 600px; margin: 0 auto; padding: 24px; background: var(--colors-white); border-radius: 8px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); } .form-actions { display: flex; gap: 12px; margin-top: 24px; padding-top: 24px; border-top: 1px solid var(--colors-grey300); } .form-actions button { min-width: 120px; } @media (max-width: 768px) { .data-element-form { margin: 16px; padding: 16px; } .form-actions { flex-direction: column; } .form-actions button { width: 100%; } } ``` #### 3. DataElementForm.test.tsx ```typescript import React from 'react' import { render, screen, fireEvent, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' import { DataElementForm } from './DataElementForm' // Mock the DHIS2 hooks jest.mock('@dhis2/app-runtime', () => ({ useDataMutation: () => [ jest.fn().mockResolvedValue({ success: true }), { loading: false } ] })) describe('DataElementForm', () => { const mockOnSubmit = jest.fn() beforeEach(() => { mockOnSubmit.mockClear() }) it('renders all form fields', () => { render(<DataElementForm onSubmit={mockOnSubmit} />) expect(screen.getByLabelText(/name/i)).toBeInTheDocument() expect(screen.getByLabelText(/short name/i)).toBeInTheDocument() expect(screen.getByLabelText(/description/i)).toBeInTheDocument() expect(screen.getByLabelText(/value type/i)).toBeInTheDocument() expect(screen.getByLabelText(/aggregation type/i)).toBeInTheDocument() expect(screen.getByLabelText(/active/i)).toBeInTheDocument() }) it('validates required fields', async () => { render(<DataElementForm onSubmit={mockOnSubmit} />) const submitButton = screen.getByRole('button', { name: /save/i }) fireEvent.click(submitButton) await waitFor(() => { expect(screen.getByText(/name is required/i)).toBeInTheDocument() expect(screen.getByText(/short name is required/i)).toBeInTheDocument() }) expect(mockOnSubmit).not.toHaveBeenCalled() }) it('submits form with valid data', async () => { const user = userEvent.setup() render(<DataElementForm onSubmit={mockOnSubmit} />) await user.type(screen.getByLabelText(/name/i), 'Test Data Element') await user.type(screen.getByLabelText(/short name/i), 'TEST_DE') await user.type(screen.getByLabelText(/description/i), 'Test description') const submitButton = screen.getByRole('button', { name: /save/i }) await user.click(submitButton) await waitFor(() => { expect(mockOnSubmit).toHaveBeenCalledWith({ success: true }) }) }) it('populates form with initial values', () => { const initialValues = { name: 'Existing Element', shortName: 'EXISTING', description: 'Existing description' } render(<DataElementForm onSubmit={mockOnSubmit} initialValues={initialValues} />) expect(screen.getByDisplayValue('Existing Element')).toBeInTheDocument() expect(screen.getByDisplayValue('EXISTING')).toBeInTheDocument() expect(screen.getByDisplayValue('Existing description')).toBeInTheDocument() }) }) ``` #### 4. Usage Example ```typescript import React, { useState } from 'react' import { DataElementForm } from './components/DataElementForm' import { AlertBar } from '@dhis2/ui' export const DataElementsPage: React.FC = () => { const [showForm, setShowForm] = useState(false) const [message, setMessage] = useState<string | null>(null) const handleFormSubmit = (result: any) => { setMessage('Data element created successfully!') setShowForm(false) setTimeout(() => setMessage(null), 5000) } return ( <div className="data-elements-page"> <h1>Data Elements Management</h1> {message && ( <AlertBar success duration={5000}> {message} </AlertBar> )} {showForm ? ( <DataElementForm onSubmit={handleFormSubmit} /> ) : ( <Button primary onClick={() => setShowForm(true)}> Create New Data Element </Button> )} </div> ) } ``` ## Features Included ✅ **Form Validation**: Real-time field validation with error messages ✅ **DHIS2 UI Integration**: Uses @dhis2/ui components for consistency ✅ **TypeScript Support**: Full type safety and IntelliSense ✅ **Responsive Design**: Mobile-friendly with CSS modules ✅ **Data Integration**: Uses @dhis2/app-runtime for API calls ✅ **Testing Setup**: Comprehensive test suite with Jest/Testing Library ✅ **Accessibility**: Proper labels and ARIA attributes ✅ **Multi-select Support**: Category selection with MultiSelect ✅ **Date Handling**: DatePicker for date-type fields ✅ **Loading States**: Proper loading and disabled states ## Additional Components Available The DHIS2 MCP server can generate many more UI components: ### Data Display Components - `DataTable`: Sortable, filterable data tables - `Charts`: Various chart types with D3.js/Chart.js - `Cards`: Information cards with actions - `Lists`: Interactive lists with search ### Form Components - `SearchForm`: Advanced search with filters - `BulkEditForm`: Mass data editing - `ValidationForm`: Complex validation patterns - `WizardForm`: Multi-step form workflows ### Navigation Components - `Sidebar`: Collapsible navigation sidebar - `Breadcrumbs`: Navigation breadcrumbs - `Tabs`: Tabbed interfaces - `Modals`: Dialog and modal patterns All generated with DHIS2 best practices and no API connection required! 🚀

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Dradebo/dhis2-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server