409 lines
9.4 KiB
TypeScript

import { createResource, createSignal, For, Show } from 'solid-js';
import {
deleteDemoPersonsById,
type GetDemoPersonsResponse,
getDemoPersons,
patchDemoPersonsById,
postDemoPersons,
} from './api';
type Person = GetDemoPersonsResponse[number];
export function PersonCRUD() {
const [editingId, setEditingId] = createSignal<number | null>(null);
const [isCreating, setIsCreating] = createSignal(false);
// Fetch persons
const [persons, { refetch }] = createResource<Person[]>(async () => {
const response = await getDemoPersons();
return response.data ?? [];
});
// Form state
const [formData, setFormData] = createSignal({
first_name: '',
last_name: '',
gender: 'other' as 'man' | 'woman' | 'other',
metadata: {
login_at: new Date().toISOString(),
ip: null as string | null,
agent: null as string | null,
plan: 'free' as 'free' | 'premium',
},
});
const resetForm = () => {
setFormData({
first_name: '',
last_name: '',
gender: 'other',
metadata: {
login_at: new Date().toISOString(),
ip: null,
agent: null,
plan: 'free',
},
});
setEditingId(null);
setIsCreating(false);
};
const handleCreate = async (e: Event) => {
e.preventDefault();
try {
await postDemoPersons({
body: formData(),
});
resetForm();
refetch();
} catch (error) {
console.error('Failed to create person:', error);
}
};
const handleUpdate = async (e: Event) => {
e.preventDefault();
const id = editingId();
if (id === null) return;
try {
await patchDemoPersonsById({
path: { id },
body: formData(),
});
resetForm();
refetch();
} catch (error) {
console.error('Failed to update person:', error);
}
};
const handleDelete = async (id: number) => {
if (!confirm('Are you sure you want to delete this person?')) return;
try {
await deleteDemoPersonsById({
path: { id },
});
refetch();
} catch (error) {
console.error('Failed to delete person:', error);
}
};
const startEdit = (person: Person) => {
setFormData({
first_name: person.first_name,
last_name: person.last_name ?? '',
gender: person.gender,
metadata: person.metadata,
});
setEditingId(person.id);
setIsCreating(false);
};
const startCreate = () => {
resetForm();
setIsCreating(true);
};
return (
<div style={{ padding: '20px', 'max-width': '1200px', margin: '0 auto' }}>
<h2>Person Management</h2>
{/* Create/Edit Form */}
<Show when={isCreating() || editingId() !== null}>
<div
style={{
border: '1px solid #ccc',
padding: '20px',
'margin-bottom': '20px',
'border-radius': '8px',
background: '#f9f9f9',
}}
>
<h3>{editingId() !== null ? 'Edit Person' : 'Create Person'}</h3>
<form onSubmit={editingId() !== null ? handleUpdate : handleCreate}>
<div style={{ 'margin-bottom': '10px' }}>
<label style={{ display: 'block', 'margin-bottom': '5px' }}>
First Name *
<input
type="text"
value={formData().first_name}
onInput={(e) =>
setFormData({
...formData(),
first_name: e.currentTarget.value,
})
}
required
style={{
width: '100%',
padding: '8px',
'box-sizing': 'border-box',
}}
/>
</label>
</div>
<div style={{ 'margin-bottom': '10px' }}>
<label style={{ display: 'block', 'margin-bottom': '5px' }}>
Last Name
<input
type="text"
value={formData().last_name}
onInput={(e) =>
setFormData({
...formData(),
last_name: e.currentTarget.value,
})
}
style={{
width: '100%',
padding: '8px',
'box-sizing': 'border-box',
}}
/>
</label>
</div>
<div style={{ 'margin-bottom': '10px' }}>
<label style={{ display: 'block', 'margin-bottom': '5px' }}>
Gender *
<select
value={formData().gender}
onChange={(e) =>
setFormData({
...formData(),
gender: e.currentTarget.value as
| 'man'
| 'woman'
| 'other',
})
}
style={{
width: '100%',
padding: '8px',
'box-sizing': 'border-box',
}}
>
<option value="man">Man</option>
<option value="woman">Woman</option>
<option value="other">Other</option>
</select>
</label>
</div>
<div style={{ 'margin-bottom': '10px' }}>
<label style={{ display: 'block', 'margin-bottom': '5px' }}>
Plan
<select
value={formData().metadata.plan}
onChange={(e) =>
setFormData({
...formData(),
metadata: {
...formData().metadata,
plan: e.currentTarget.value as 'free' | 'premium',
},
})
}
style={{
width: '100%',
padding: '8px',
'box-sizing': 'border-box',
}}
>
<option value="free">Free</option>
<option value="premium">Premium</option>
</select>
</label>
</div>
<div style={{ display: 'flex', gap: '10px' }}>
<button
type="submit"
style={{ padding: '8px 16px', cursor: 'pointer' }}
>
{editingId() !== null ? 'Update' : 'Create'}
</button>
<button
type="button"
onClick={resetForm}
style={{ padding: '8px 16px', cursor: 'pointer' }}
>
Cancel
</button>
</div>
</form>
</div>
</Show>
{/* Create Button */}
<Show when={!isCreating() && editingId() === null}>
<button
type="button"
onClick={startCreate}
style={{
padding: '10px 20px',
'margin-bottom': '20px',
cursor: 'pointer',
background: '#4CAF50',
color: 'white',
border: 'none',
'border-radius': '4px',
}}
>
+ Create New Person
</button>
</Show>
{/* List */}
<div>
<Show when={persons.loading}>
<p>Loading persons...</p>
</Show>
<Show when={persons.error}>
<p style={{ color: 'red' }}>Error loading persons: {persons.error.message}</p>
</Show>
<Show when={persons()}>
<table
style={{
width: '100%',
'border-collapse': 'collapse',
border: '1px solid #ddd',
}}
>
<thead>
<tr style={{ background: '#f2f2f2' }}>
<th
style={{
padding: '12px',
'text-align': 'left',
border: '1px solid #ddd',
}}
>
ID
</th>
<th
style={{
padding: '12px',
'text-align': 'left',
border: '1px solid #ddd',
}}
>
First Name
</th>
<th
style={{
padding: '12px',
'text-align': 'left',
border: '1px solid #ddd',
}}
>
Last Name
</th>
<th
style={{
padding: '12px',
'text-align': 'left',
border: '1px solid #ddd',
}}
>
Gender
</th>
<th
style={{
padding: '12px',
'text-align': 'left',
border: '1px solid #ddd',
}}
>
Plan
</th>
<th
style={{
padding: '12px',
'text-align': 'left',
border: '1px solid #ddd',
}}
>
Created At
</th>
<th
style={{
padding: '12px',
'text-align': 'left',
border: '1px solid #ddd',
}}
>
Actions
</th>
</tr>
</thead>
<tbody>
<For each={persons()}>
{(person) => (
<tr>
<td style={{ padding: '12px', border: '1px solid #ddd' }}>
{person.id}
</td>
<td style={{ padding: '12px', border: '1px solid #ddd' }}>
{person.first_name}
</td>
<td style={{ padding: '12px', border: '1px solid #ddd' }}>
{person.last_name ?? '-'}
</td>
<td style={{ padding: '12px', border: '1px solid #ddd' }}>
{person.gender}
</td>
<td style={{ padding: '12px', border: '1px solid #ddd' }}>
{person.metadata.plan}
</td>
<td style={{ padding: '12px', border: '1px solid #ddd' }}>
{new Date(person.created_at).toLocaleDateString()}
</td>
<td style={{ padding: '12px', border: '1px solid #ddd' }}>
<button
type="button"
onClick={() => startEdit(person)}
style={{
padding: '6px 12px',
'margin-right': '5px',
cursor: 'pointer',
background: '#2196F3',
color: 'white',
border: 'none',
'border-radius': '4px',
}}
>
Edit
</button>
<button
type="button"
onClick={() => handleDelete(person.id)}
style={{
padding: '6px 12px',
cursor: 'pointer',
background: '#f44336',
color: 'white',
border: 'none',
'border-radius': '4px',
}}
>
Delete
</button>
</td>
</tr>
)}
</For>
</tbody>
</table>
</Show>
</div>
</div>
);
}