Compare commits
2 Commits
2897172213
...
d399668932
| Author | SHA1 | Date | |
|---|---|---|---|
| d399668932 | |||
| 208538f930 |
@@ -5,7 +5,7 @@ import type { Milestone, Project, Task } from '@/types'
|
|||||||
const TASK_TYPES = [
|
const TASK_TYPES = [
|
||||||
{ value: 'story', label: 'Story', subtypes: ['improvement', 'refactor'] }, // P9.6: 'feature' removed — must come from propose accept
|
{ value: 'story', label: 'Story', subtypes: ['improvement', 'refactor'] }, // P9.6: 'feature' removed — must come from propose accept
|
||||||
{ value: 'issue', label: 'Issue', subtypes: ['infrastructure', 'performance', 'regression', 'security', 'user_experience', 'defect'] },
|
{ value: 'issue', label: 'Issue', subtypes: ['infrastructure', 'performance', 'regression', 'security', 'user_experience', 'defect'] },
|
||||||
{ value: 'task', label: 'Task', subtypes: ['defect'] },
|
// P7.1: 'task' type removed — defect subtype migrated to issue/defect
|
||||||
{ value: 'test', label: 'Test', subtypes: ['regression', 'security', 'smoke', 'stress'] },
|
{ value: 'test', label: 'Test', subtypes: ['regression', 'security', 'smoke', 'stress'] },
|
||||||
{ value: 'maintenance', label: 'Maintenance', subtypes: ['deploy'] }, // P9.6: 'release' removed — controlled via milestone flow
|
{ value: 'maintenance', label: 'Maintenance', subtypes: ['deploy'] }, // P9.6: 'release' removed — controlled via milestone flow
|
||||||
{ value: 'research', label: 'Research', subtypes: [] },
|
{ value: 'research', label: 'Research', subtypes: [] },
|
||||||
@@ -42,7 +42,7 @@ const makeInitialForm = (projectId = 0, milestoneId = 0): FormState => ({
|
|||||||
description: '',
|
description: '',
|
||||||
project_id: projectId,
|
project_id: projectId,
|
||||||
milestone_id: milestoneId,
|
milestone_id: milestoneId,
|
||||||
task_type: 'task',
|
task_type: 'issue', // P7.1: default changed from 'task' to 'issue'
|
||||||
task_subtype: '',
|
task_subtype: '',
|
||||||
priority: 'medium',
|
priority: 'medium',
|
||||||
tags: '',
|
tags: '',
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import type { Project, Milestone } from '@/types'
|
|||||||
const TASK_TYPES = [
|
const TASK_TYPES = [
|
||||||
{ value: 'story', label: 'Story', subtypes: ['improvement', 'refactor'] }, // P9.6: 'feature' removed — must come from propose accept
|
{ value: 'story', label: 'Story', subtypes: ['improvement', 'refactor'] }, // P9.6: 'feature' removed — must come from propose accept
|
||||||
{ value: 'issue', label: 'Issue', subtypes: ['infrastructure', 'performance', 'regression', 'security', 'user_experience', 'defect'] },
|
{ value: 'issue', label: 'Issue', subtypes: ['infrastructure', 'performance', 'regression', 'security', 'user_experience', 'defect'] },
|
||||||
{ value: 'task', label: 'Task', subtypes: ['defect'] },
|
// P7.1: 'task' type removed — defect subtype migrated to issue/defect
|
||||||
{ value: 'test', label: 'Test', subtypes: ['regression', 'security', 'smoke', 'stress'] },
|
{ value: 'test', label: 'Test', subtypes: ['regression', 'security', 'smoke', 'stress'] },
|
||||||
{ value: 'maintenance', label: 'Maintenance', subtypes: ['deploy'] }, // P9.6: 'release' removed — controlled via milestone flow
|
{ value: 'maintenance', label: 'Maintenance', subtypes: ['deploy'] }, // P9.6: 'release' removed — controlled via milestone flow
|
||||||
{ value: 'research', label: 'Research', subtypes: [] },
|
{ value: 'research', label: 'Research', subtypes: [] },
|
||||||
@@ -19,7 +19,7 @@ export default function CreateTaskPage() {
|
|||||||
const [projects, setProjects] = useState<Project[]>([])
|
const [projects, setProjects] = useState<Project[]>([])
|
||||||
const [milestones, setMilestones] = useState<Milestone[]>([])
|
const [milestones, setMilestones] = useState<Milestone[]>([])
|
||||||
const [form, setForm] = useState({
|
const [form, setForm] = useState({
|
||||||
title: '', description: '', project_id: 0, milestone_id: 0, task_type: 'task',
|
title: '', description: '', project_id: 0, milestone_id: 0, task_type: 'issue', // P7.1: default changed from 'task' to 'issue'
|
||||||
task_subtype: '', priority: 'medium', tags: '', reporter_id: 1,
|
task_subtype: '', priority: 'medium', tags: '', reporter_id: 1,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,12 @@ export default function ProposeDetailPage() {
|
|||||||
const [actionLoading, setActionLoading] = useState(false)
|
const [actionLoading, setActionLoading] = useState(false)
|
||||||
const [error, setError] = useState('')
|
const [error, setError] = useState('')
|
||||||
|
|
||||||
|
// Edit state (P10.7)
|
||||||
|
const [showEdit, setShowEdit] = useState(false)
|
||||||
|
const [editTitle, setEditTitle] = useState('')
|
||||||
|
const [editDescription, setEditDescription] = useState('')
|
||||||
|
const [editLoading, setEditLoading] = useState(false)
|
||||||
|
|
||||||
const fetchPropose = () => {
|
const fetchPropose = () => {
|
||||||
if (!projectId) return
|
if (!projectId) return
|
||||||
api.get<Propose>(`/projects/${projectId}/proposes/${id}`).then(({ data }) => setPropose(data))
|
api.get<Propose>(`/projects/${projectId}/proposes/${id}`).then(({ data }) => setPropose(data))
|
||||||
@@ -62,6 +68,32 @@ export default function ProposeDetailPage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const openEditModal = () => {
|
||||||
|
if (!propose) return
|
||||||
|
setEditTitle(propose.title)
|
||||||
|
setEditDescription(propose.description || '')
|
||||||
|
setError('')
|
||||||
|
setShowEdit(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleEdit = async () => {
|
||||||
|
if (!projectId) return
|
||||||
|
setEditLoading(true)
|
||||||
|
setError('')
|
||||||
|
try {
|
||||||
|
await api.patch(`/projects/${projectId}/proposes/${id}`, {
|
||||||
|
title: editTitle,
|
||||||
|
description: editDescription,
|
||||||
|
})
|
||||||
|
setShowEdit(false)
|
||||||
|
fetchPropose()
|
||||||
|
} catch (err: any) {
|
||||||
|
setError(err.response?.data?.detail || 'Update failed')
|
||||||
|
} finally {
|
||||||
|
setEditLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleReopen = async () => {
|
const handleReopen = async () => {
|
||||||
if (!projectId) return
|
if (!projectId) return
|
||||||
setActionLoading(true)
|
setActionLoading(true)
|
||||||
@@ -122,6 +154,12 @@ export default function ProposeDetailPage() {
|
|||||||
<div className="section" style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
|
<div className="section" style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
|
||||||
{propose.status === 'open' && (
|
{propose.status === 'open' && (
|
||||||
<>
|
<>
|
||||||
|
<button
|
||||||
|
className="btn-transition"
|
||||||
|
onClick={openEditModal}
|
||||||
|
>
|
||||||
|
✏️ Edit
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
className="btn-primary"
|
className="btn-primary"
|
||||||
disabled={actionLoading}
|
disabled={actionLoading}
|
||||||
@@ -154,6 +192,43 @@ export default function ProposeDetailPage() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Edit modal (P10.7 — only reachable when open) */}
|
||||||
|
{showEdit && (
|
||||||
|
<div className="modal-overlay" onClick={() => setShowEdit(false)}>
|
||||||
|
<div className="modal" onClick={(e) => e.stopPropagation()}>
|
||||||
|
<h3>Edit Propose</h3>
|
||||||
|
<label style={{ display: 'block', marginBottom: 8 }}>
|
||||||
|
<strong>Title</strong>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={editTitle}
|
||||||
|
onChange={(e) => setEditTitle(e.target.value)}
|
||||||
|
style={{ width: '100%', marginTop: 4 }}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label style={{ display: 'block', marginBottom: 8 }}>
|
||||||
|
<strong>Description</strong>
|
||||||
|
<textarea
|
||||||
|
value={editDescription}
|
||||||
|
onChange={(e) => setEditDescription(e.target.value)}
|
||||||
|
rows={6}
|
||||||
|
style={{ width: '100%', marginTop: 4 }}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<div style={{ marginTop: 12, display: 'flex', gap: 8 }}>
|
||||||
|
<button
|
||||||
|
className="btn-primary"
|
||||||
|
onClick={handleEdit}
|
||||||
|
disabled={!editTitle.trim() || editLoading}
|
||||||
|
>
|
||||||
|
{editLoading ? 'Saving...' : 'Save'}
|
||||||
|
</button>
|
||||||
|
<button className="btn-back" onClick={() => setShowEdit(false)}>Cancel</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Accept modal with milestone selector */}
|
{/* Accept modal with milestone selector */}
|
||||||
{showAccept && (
|
{showAccept && (
|
||||||
<div className="modal-overlay" onClick={() => setShowAccept(false)}>
|
<div className="modal-overlay" onClick={() => setShowAccept(false)}>
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export interface Task {
|
|||||||
id: number
|
id: number
|
||||||
title: string
|
title: string
|
||||||
description: string | null
|
description: string | null
|
||||||
task_type: 'issue' | 'maintenance' | 'research' | 'review' | 'story' | 'test' | 'resolution' | 'task'
|
task_type: 'issue' | 'maintenance' | 'research' | 'review' | 'story' | 'test' | 'resolution' // P7.1: 'task' removed
|
||||||
task_subtype: string | null
|
task_subtype: string | null
|
||||||
status: 'open' | 'pending' | 'undergoing' | 'completed' | 'closed'
|
status: 'open' | 'pending' | 'undergoing' | 'completed' | 'closed'
|
||||||
priority: 'low' | 'medium' | 'high' | 'critical'
|
priority: 'low' | 'medium' | 'high' | 'critical'
|
||||||
|
|||||||
Reference in New Issue
Block a user