feat: zodpovedna osoba za projekt - novy sloupec + editace
Pridano pole responsible_user_id do tabulky projects s FK na users. Select zodpovedne osoby v ProjectDetail, ProjectCreate a sloupec v seznamu projektu. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -20,8 +20,10 @@ export default function ProjectCreate() {
|
||||
name: '',
|
||||
customer_id: null,
|
||||
customer_name: '',
|
||||
start_date: new Date().toISOString().split('T')[0]
|
||||
start_date: new Date().toISOString().split('T')[0],
|
||||
responsible_user_id: ''
|
||||
})
|
||||
const [users, setUsers] = useState([])
|
||||
const [saving, setSaving] = useState(false)
|
||||
const [errors, setErrors] = useState({})
|
||||
const [loadingNumber, setLoadingNumber] = useState(true)
|
||||
@@ -35,9 +37,10 @@ export default function ProjectCreate() {
|
||||
useEffect(() => {
|
||||
const load = async () => {
|
||||
try {
|
||||
const [numRes, custRes] = await Promise.all([
|
||||
const [numRes, custRes, usersRes] = await Promise.all([
|
||||
apiFetch(`${API_BASE}/projects.php?action=next_number`),
|
||||
apiFetch(`${API_BASE}/customers.php`)
|
||||
apiFetch(`${API_BASE}/customers.php`),
|
||||
apiFetch(`${API_BASE}/projects.php?action=users`)
|
||||
])
|
||||
|
||||
const numData = await numRes.json()
|
||||
@@ -49,6 +52,11 @@ export default function ProjectCreate() {
|
||||
if (custData.success) {
|
||||
setCustomers(custData.data.customers)
|
||||
}
|
||||
|
||||
const usersData = await usersRes.json()
|
||||
if (usersData.success) {
|
||||
setUsers(usersData.data.users)
|
||||
}
|
||||
} catch {
|
||||
alert.error('Chyba při načítání dat')
|
||||
} finally {
|
||||
@@ -109,7 +117,8 @@ export default function ProjectCreate() {
|
||||
name: form.name.trim(),
|
||||
customer_id: form.customer_id,
|
||||
start_date: form.start_date,
|
||||
project_number: form.project_number.trim()
|
||||
project_number: form.project_number.trim(),
|
||||
responsible_user_id: form.responsible_user_id || null
|
||||
}
|
||||
|
||||
const res = await apiFetch(`${API_BASE}/projects.php`, {
|
||||
@@ -263,6 +272,21 @@ export default function ProjectCreate() {
|
||||
/>
|
||||
</FormField>
|
||||
</div>
|
||||
|
||||
<div className="admin-form-row">
|
||||
<FormField label="Zodpovědná osoba">
|
||||
<select
|
||||
value={form.responsible_user_id}
|
||||
onChange={(e) => updateForm('responsible_user_id', e.target.value)}
|
||||
className="admin-form-select"
|
||||
>
|
||||
<option value="">— Nevybráno —</option>
|
||||
{users.map(u => (
|
||||
<option key={u.id} value={u.id}>{u.name}</option>
|
||||
))}
|
||||
</select>
|
||||
</FormField>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
@@ -42,8 +42,10 @@ export default function ProjectDetail() {
|
||||
name: '',
|
||||
status: 'aktivni',
|
||||
start_date: '',
|
||||
end_date: ''
|
||||
end_date: '',
|
||||
responsible_user_id: ''
|
||||
})
|
||||
const [users, setUsers] = useState([])
|
||||
|
||||
const [deleteConfirm, setDeleteConfirm] = useState(false)
|
||||
const [deleting, setDeleting] = useState(false)
|
||||
@@ -91,7 +93,8 @@ export default function ProjectDetail() {
|
||||
name: p.name || '',
|
||||
status: p.status || 'aktivni',
|
||||
start_date: (p.start_date || '').substring(0, 10),
|
||||
end_date: (p.end_date || '').substring(0, 10)
|
||||
end_date: (p.end_date || '').substring(0, 10),
|
||||
responsible_user_id: p.responsible_user_id || ''
|
||||
})
|
||||
} else {
|
||||
alert.error(result.error || 'Nepodařilo se načíst projekt')
|
||||
@@ -104,8 +107,22 @@ export default function ProjectDetail() {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
const fetchUsers = async () => {
|
||||
try {
|
||||
const res = await apiFetch(`${API_BASE}/projects.php?action=users`)
|
||||
if (res.status === 401) return
|
||||
const data = await res.json()
|
||||
if (data.success) {
|
||||
setUsers(data.data.users || [])
|
||||
}
|
||||
} catch {
|
||||
// silent
|
||||
}
|
||||
}
|
||||
|
||||
fetchDetail()
|
||||
fetchNotes()
|
||||
fetchUsers()
|
||||
}, [id, alert, navigate]) // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
if (!hasPermission('projects.view')) return <Forbidden />
|
||||
@@ -127,7 +144,8 @@ export default function ProjectDetail() {
|
||||
name: form.name,
|
||||
status: form.status,
|
||||
start_date: form.start_date || null,
|
||||
end_date: form.end_date || null
|
||||
end_date: form.end_date || null,
|
||||
responsible_user_id: form.responsible_user_id || null
|
||||
})
|
||||
})
|
||||
const result = await response.json()
|
||||
@@ -332,6 +350,22 @@ export default function ProjectDetail() {
|
||||
style={{ backgroundColor: 'var(--bg-secondary)', cursor: 'default' }}
|
||||
/>
|
||||
</FormField>
|
||||
<FormField label="Zodpovědná osoba">
|
||||
<select
|
||||
value={form.responsible_user_id}
|
||||
onChange={(e) => updateForm('responsible_user_id', e.target.value)}
|
||||
className="admin-form-select"
|
||||
disabled={!canEdit}
|
||||
>
|
||||
<option value="">— Nevybráno —</option>
|
||||
{users.map(u => (
|
||||
<option key={u.id} value={u.id}>{u.name}</option>
|
||||
))}
|
||||
</select>
|
||||
</FormField>
|
||||
</div>
|
||||
|
||||
<div className="admin-form-row admin-form-row-3">
|
||||
<FormField label="Stav">
|
||||
<select
|
||||
value={form.status}
|
||||
@@ -344,9 +378,6 @@ export default function ProjectDetail() {
|
||||
<option value="zruseny">Zrušený</option>
|
||||
</select>
|
||||
</FormField>
|
||||
</div>
|
||||
|
||||
<div className="admin-form-row">
|
||||
<FormField label="Datum zahájení">
|
||||
<AdminDatePicker
|
||||
mode="date"
|
||||
|
||||
@@ -189,6 +189,7 @@ export default function Projects() {
|
||||
Název <SortIcon column="name" sort={activeSort} order={order} />
|
||||
</th>
|
||||
<th>Zákazník</th>
|
||||
<th>Zodpovědná osoba</th>
|
||||
<th style={{ cursor: 'pointer' }} onClick={() => handleSort('status')}>
|
||||
Stav <SortIcon column="status" sort={activeSort} order={order} />
|
||||
</th>
|
||||
@@ -212,6 +213,7 @@ export default function Projects() {
|
||||
</td>
|
||||
<td className="fw-500">{p.name || '—'}</td>
|
||||
<td>{p.customer_name || '—'}</td>
|
||||
<td>{p.responsible_user_name || '—'}</td>
|
||||
<td>
|
||||
<span className={`admin-badge ${STATUS_CLASSES[p.status] || ''}`}>
|
||||
{STATUS_LABELS[p.status] || p.status}
|
||||
|
||||
Reference in New Issue
Block a user