App.jsx — プロバイダー、テーマ、テーブル、スケジュール設定用ドロワー
ネイティブの Mantine を使った完全な例です。テーマのオーバーライド、useForm による検証、DatePickerInput、テーブルの更新、同意ステータスの変更が含まれます。
import React, {useState} from 'react';
import {MantineProvider, createTheme, Box, Group, Stack, Title, Text, Button, Paper, Badge, Table, Drawer, TextInput, Select, Checkbox, Alert, SimpleGrid} from '@mantine/core';
import {useForm} from '@mantine/form';
import {DatePickerInput} from '@mantine/dates';
import '@mantine/core/styles.css';
import '@mantine/dates/styles.css';
const theme=createTheme({primaryColor:'cyan',primaryShade:8,defaultRadius:'md',components:{Button:Button.extend({defaultProps:{size:'sm'}}),TextInput:TextInput.extend({defaultProps:{size:'md'}})}});
function Tracker(){
const[opened,setOpened]=useState(false),[notice,setNotice]=useState(''),[rows,setRows]=useState([{name:'Alex Morgan',study:'Navigation study',date:'2026-10-14',status:'Confirmed'},{name:'Sam Rivera',study:'Editor study',date:'2026-10-16',status:'Confirmed'}]);
const form=useForm({initialValues:{name:'',study:'Navigation study',date:null,consent:false},validate:{name:v=>v.trim().length<2?'Enter a name with at least two characters.':null,date:v=>!v?'Choose a session date.':null,consent:v=>!v?'Confirm consent before scheduling this example.':null}});
const submit=values=>{setRows([...rows,{name:values.name.trim(),study:values.study,date:values.date,status:'Confirmed'}]);setOpened(false);setNotice('Participant added in this browser only. No research record was stored.');form.reset();};
return <Box p={{base:'md',sm:'xl'}} bg='gray.0'><Box maw={1040} mx='auto'>
<Group justify='space-between' align='flex-start' mb='xl'><Box><Text c='cyan.9' fw={700} size='xs' tt='uppercase' mb='xs'>Mantine · Research operations</Text><Title order={1}>Participant sessions</Title><Text c='dimmed' mt='xs'>Consent and scheduling stay visible in the same working view.</Text></Box><Button onClick={()=>{form.reset();setOpened(true)}}>Add participant</Button></Group>
<SimpleGrid cols={{base:1,sm:3}} mb='xl'><Paper withBorder p='lg'><Text size='sm' c='dimmed'>In this preview</Text><Text fz={32} fw={700}>{rows.length}</Text><Text size='sm'>Participant records</Text></Paper><Paper withBorder p='lg'><Text size='sm' c='dimmed'>Scheduled</Text><Text fz={32} fw={700}>{rows.filter(r=>r.status==='Confirmed').length}</Text><Text size='sm'>With confirmed example consent</Text></Paper><Paper withBorder p='lg' bg='cyan.0'><Text size='sm' c='cyan.9' fw={600}>Working theme</Text><Text mt='xs' size='sm'>cyan.8 action · radius md<br/>spacing lg · body md</Text></Paper></SimpleGrid>
{notice&&<Alert title='Local change' color='cyan' mb='md' withCloseButton onClose={()=>setNotice('')}>{notice}</Alert>}
<Paper withBorder radius='md'><Table.ScrollContainer minWidth={580}><Table verticalSpacing='md' horizontalSpacing='lg' highlightOnHover><Table.Thead><Table.Tr><Table.Th>Participant</Table.Th><Table.Th>Study / session</Table.Th><Table.Th>Consent</Table.Th><Table.Th>Action</Table.Th></Table.Tr></Table.Thead><Table.Tbody>{rows.map((r,i)=><Table.Tr key={i}><Table.Td><Text fw={600} size='sm'>{r.name}</Text><Text size='xs' c='dimmed'>P-{String(i+1).padStart(3,'0')}</Text></Table.Td><Table.Td><Text size='sm'>{r.study}</Text><Text size='xs' c='dimmed'>{r.date}</Text></Table.Td><Table.Td><Badge variant='light' color={r.status==='Confirmed'?'cyan':'orange'}>{r.status}</Badge></Table.Td><Table.Td><Button variant='subtle' color='gray' disabled={r.status==='Withdrawn'} aria-label={'Withdraw consent for '+r.name} onClick={()=>{setRows(rows.map((x,j)=>j===i?{...x,status:'Withdrawn'}:x));setNotice('Consent withdrawn in the preview. The prior session remains visible as history.')}}>Withdraw</Button></Table.Td></Table.Tr>)}</Table.Tbody></Table></Table.ScrollContainer></Paper>
<Text size='xs' c='dimmed' mt='lg'>Native Mantine 9.6.3: Table, Drawer, useForm, DatePickerInput, and input error styles. All records are memory-only examples.</Text>
<Drawer opened={opened} onClose={()=>setOpened(false)} position='right' size='md' title={<Title order={2} size='h3'>Schedule a participant</Title>}><form onSubmit={form.onSubmit(submit)} noValidate><Stack gap='lg'><Text size='sm' c='dimmed'>All fields below belong to this one study enrollment. Closing discards the draft.</Text><TextInput label='Participant name' placeholder='Jules Davis' data-autofocus {...form.getInputProps('name')}/><Select label='Study' data={['Navigation study','Editor study']} allowDeselect={false} {...form.getInputProps('study')}/><DatePickerInput label='Session date' placeholder='Choose a date' valueFormat='DD MMM YYYY' defaultDate='2026-10-01' {...form.getInputProps('date')}/><Checkbox label='Consent is confirmed for this example study' {...form.getInputProps('consent',{type:'checkbox'})}/><Button type='submit'>Add to preview</Button><Text size='xs' c='dimmed'>This checkbox demonstrates validation; it is not a research consent collection process.</Text></Stack></form></Drawer>
</Box></Box>;
}
export default function App(){return <MantineProvider theme={theme} forceColorScheme='light'><Tracker/></MantineProvider>}
- core と dates の両方の CSS をインポートしています。
- 同意チェックボックスは検証条件の例であり、実際に同意を取得するフローではありません。


