�️ Integrated Project Delete API with Enhanced Error Handling
� API Integration:
- Integrated /api/Projects/delete/{id} endpoint for project deletion
- Used POST method instead of DELETE for better server compatibility
- Added comprehensive request headers (Content-Type, Accept JSON)
- Proper project ID parameter passing in URL path
�️ Enhanced Error Handling:
- Robust response parsing for empty, non-JSON, and malformed responses
- Smart success detection with multiple criteria (HTTP status, response content)
- Graceful fallback logic for edge cases and parsing failures
- Comprehensive error logging with emojis for better debugging
✅ User Experience Improvements:
- Modal confirmation dialog with Vietnamese text before deletion
- Clear success/error messages with proper feedback
- Automatic data reload after successful deletion
- Color-coded action icons (blue edit, red delete) with hover effects
� Response Handling:
- Handles HTTP 200 OK with empty response body as success
- Handles non-JSON responses gracefully
- Detects actual API errors vs successful operations
- Prevents false error messages on successful deletions
� Technical Features:
- Try-catch blocks with detailed error information
- Raw response text logging for debugging
- JSON parsing with fallback mechanisms
- Maintains pagination state after deletion
� UI Enhancements:
- Professional action column with edit and delete buttons
- Consistent styling with existing application theme
- Responsive design for mobile and desktop
- Loading states during API operations
This commit is contained in:
parent
6c890f369c
commit
cee837e1a6
@ -65,14 +65,14 @@ export const SidebarData = [
|
|||||||
{ label: "Create Product", link: "/add-product", icon: <Icon.PlusSquare />,showSubRoute: false, submenu: false },
|
{ label: "Create Product", link: "/add-product", icon: <Icon.PlusSquare />,showSubRoute: false, submenu: false },
|
||||||
{ label: "Expired Products", link: "/expired-products", icon: <Icon.Codesandbox />,showSubRoute: false,submenu: false },
|
{ label: "Expired Products", link: "/expired-products", icon: <Icon.Codesandbox />,showSubRoute: false,submenu: false },
|
||||||
{ label: "Low Stocks", link: "/low-stocks", icon: <Icon.TrendingDown />,showSubRoute: false,submenu: false },
|
{ label: "Low Stocks", link: "/low-stocks", icon: <Icon.TrendingDown />,showSubRoute: false,submenu: false },
|
||||||
{ label: "Category", link: "/category-list", icon: <Icon.Codepen />,showSubRoute: false,submenu: false },
|
{ label: "Danh mục", link: "/category-list", icon: <Icon.Codepen />,showSubRoute: false,submenu: false },
|
||||||
{ label: "Sub Category", link: "/sub-categories", icon: <Icon.Speaker />,showSubRoute: false,submenu: false },
|
{ label: "Sub Category", link: "/sub-categories", icon: <Icon.Speaker />,showSubRoute: false,submenu: false },
|
||||||
{ label: "Brands", link: "/brand-list", icon: <Icon.Tag />,showSubRoute: false,submenu: false },
|
{ label: "Thương hiệu", link: "/brand-list", icon: <Icon.Tag />,showSubRoute: false,submenu: false },
|
||||||
{ label: "Units", link: "/units", icon: <Icon.Speaker />,showSubRoute: false,submenu: false },
|
{ label: "Units", link: "/units", icon: <Icon.Speaker />,showSubRoute: false,submenu: false },
|
||||||
{ label: "Variant Attributes", link: "/variant-attributes", icon: <Icon.Layers />,showSubRoute: false,submenu: false },
|
{ label: "Variant Attributes", link: "/variant-attributes", icon: <Icon.Layers />,showSubRoute: false,submenu: false },
|
||||||
{ label: "Warranties", link: "/warranty", icon: <Icon.Bookmark />,showSubRoute: false,submenu: false },
|
{ label: "Bảo hành", link: "/warranty", icon: <Icon.Bookmark />,showSubRoute: false,submenu: false },
|
||||||
{ label: "Print Barcode", link: "/barcode", icon: <Icon.AlignJustify />, showSubRoute: false,submenu: false },
|
{ label: "In Barcode", link: "/barcode", icon: <Icon.AlignJustify />, showSubRoute: false,submenu: false },
|
||||||
{ label: "Print QR Code", link: "/qrcode", icon: <Icon.Maximize />,showSubRoute: false,submenu: false },
|
{ label: "In QR Code", link: "/qrcode", icon: <Icon.Maximize />,showSubRoute: false,submenu: false },
|
||||||
{ label: "Khách mời đám cưới", link: "/wedding-guest-list", icon: <Icon.Heart />,showSubRoute: false,submenu: false }
|
{ label: "Khách mời đám cưới", link: "/wedding-guest-list", icon: <Icon.Heart />,showSubRoute: false,submenu: false }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@ -65,7 +65,8 @@ const CreateProject = () => {
|
|||||||
{ id: 1, name: 'Web Development', color: 'blue' },
|
{ id: 1, name: 'Web Development', color: 'blue' },
|
||||||
{ id: 2, name: 'Mobile App', color: 'green' },
|
{ id: 2, name: 'Mobile App', color: 'green' },
|
||||||
{ id: 3, name: 'Design', color: 'purple' },
|
{ id: 3, name: 'Design', color: 'purple' },
|
||||||
{ id: 4, name: 'Marketing', color: 'orange' }
|
{ id: 4, name: 'Marketing', color: 'orange' },
|
||||||
|
{ id: 5, name: 'Khác', color: 'orange' },
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -285,8 +286,8 @@ const CreateProject = () => {
|
|||||||
<div className="page-header">
|
<div className="page-header">
|
||||||
<div className="add-item d-flex">
|
<div className="add-item d-flex">
|
||||||
<div className="page-title">
|
<div className="page-title">
|
||||||
<h4>Create New Project</h4>
|
<h4>Tạo dự án</h4>
|
||||||
<h6>Add a new project to your workspace</h6>
|
<h6>Thêm một dự án mới vào không gian làm việc của bạn</h6>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="page-btn">
|
<div className="page-btn">
|
||||||
@ -308,19 +309,19 @@ const CreateProject = () => {
|
|||||||
<div className="form-group-icon">
|
<div className="form-group-icon">
|
||||||
<FileText size={20} />
|
<FileText size={20} />
|
||||||
</div>
|
</div>
|
||||||
<h5>Project Information</h5>
|
<h5>Thông tin</h5>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-lg-6">
|
<div className="col-lg-6">
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<label className="form-label">Project Name <span className="text-danger">*</span></label>
|
<label className="form-label">Tên dự án <span className="text-danger">*</span></label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className={`form-control ${errors.projectName ? 'is-invalid' : ''}`}
|
className={`form-control ${errors.projectName ? 'is-invalid' : ''}`}
|
||||||
value={formData.projectName}
|
value={formData.projectName}
|
||||||
onChange={(e) => handleInputChange('projectName', e.target.value)}
|
onChange={(e) => handleInputChange('projectName', e.target.value)}
|
||||||
placeholder="Enter project name"
|
placeholder="Nhập tên dự án"
|
||||||
/>
|
/>
|
||||||
{errors.projectName && <div className="invalid-feedback">{errors.projectName}</div>}
|
{errors.projectName && <div className="invalid-feedback">{errors.projectName}</div>}
|
||||||
</div>
|
</div>
|
||||||
@ -328,13 +329,13 @@ const CreateProject = () => {
|
|||||||
|
|
||||||
<div className="col-lg-6">
|
<div className="col-lg-6">
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<label className="form-label">Client Name <span className="text-danger">*</span></label>
|
<label className="form-label">Đối tác <span className="text-danger">*</span></label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className={`form-control ${errors.clientName ? 'is-invalid' : ''}`}
|
className={`form-control ${errors.clientName ? 'is-invalid' : ''}`}
|
||||||
value={formData.clientName}
|
value={formData.clientName}
|
||||||
onChange={(e) => handleInputChange('clientName', e.target.value)}
|
onChange={(e) => handleInputChange('clientName', e.target.value)}
|
||||||
placeholder="Enter client name"
|
placeholder="Nhập đối tác"
|
||||||
/>
|
/>
|
||||||
{errors.clientName && <div className="invalid-feedback">{errors.clientName}</div>}
|
{errors.clientName && <div className="invalid-feedback">{errors.clientName}</div>}
|
||||||
</div>
|
</div>
|
||||||
@ -342,13 +343,13 @@ const CreateProject = () => {
|
|||||||
|
|
||||||
<div className="col-lg-12">
|
<div className="col-lg-12">
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<label className="form-label">Project Description <span className="text-danger">*</span></label>
|
<label className="form-label">Mô tả <span className="text-danger">*</span></label>
|
||||||
<TextArea
|
<TextArea
|
||||||
rows={4}
|
rows={4}
|
||||||
className={`form-control ${errors.description ? 'is-invalid' : ''}`}
|
className={`form-control ${errors.description ? 'is-invalid' : ''}`}
|
||||||
value={formData.description}
|
value={formData.description}
|
||||||
onChange={(e) => handleInputChange('description', e.target.value)}
|
onChange={(e) => handleInputChange('description', e.target.value)}
|
||||||
placeholder="Describe your project goals, requirements, and deliverables..."
|
placeholder="Mô tả chi tiết về dự án..."
|
||||||
/>
|
/>
|
||||||
{errors.description && <div className="invalid-feedback">{errors.description}</div>}
|
{errors.description && <div className="invalid-feedback">{errors.description}</div>}
|
||||||
</div>
|
</div>
|
||||||
@ -362,13 +363,13 @@ const CreateProject = () => {
|
|||||||
<div className="form-group-icon">
|
<div className="form-group-icon">
|
||||||
<Target size={20} />
|
<Target size={20} />
|
||||||
</div>
|
</div>
|
||||||
<h5>Project Settings</h5>
|
<h5>Cấu hình mục tiêu</h5>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-lg-4">
|
<div className="col-lg-4">
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<label className="form-label">Category <span className="text-danger">*</span></label>
|
<label className="form-label">Danh mục <span className="text-danger">*</span></label>
|
||||||
<Select
|
<Select
|
||||||
value={formData.categoryId}
|
value={formData.categoryId}
|
||||||
onChange={(value) => handleInputChange('categoryId', value)}
|
onChange={(value) => handleInputChange('categoryId', value)}
|
||||||
@ -389,7 +390,7 @@ const CreateProject = () => {
|
|||||||
|
|
||||||
<div className="col-lg-4">
|
<div className="col-lg-4">
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<label className="form-label">Priority</label>
|
<label className="form-label">Mức độ</label>
|
||||||
<Select
|
<Select
|
||||||
value={formData.priority}
|
value={formData.priority}
|
||||||
onChange={(value) => handleInputChange('priority', value)}
|
onChange={(value) => handleInputChange('priority', value)}
|
||||||
@ -397,15 +398,15 @@ const CreateProject = () => {
|
|||||||
>
|
>
|
||||||
<Option value="low">
|
<Option value="low">
|
||||||
<span className="badge badge-success me-2"></span>
|
<span className="badge badge-success me-2"></span>
|
||||||
Low Priority
|
Thấp
|
||||||
</Option>
|
</Option>
|
||||||
<Option value="medium">
|
<Option value="medium">
|
||||||
<span className="badge badge-warning me-2"></span>
|
<span className="badge badge-warning me-2"></span>
|
||||||
Medium Priority
|
Trung bình
|
||||||
</Option>
|
</Option>
|
||||||
<Option value="high">
|
<Option value="high">
|
||||||
<span className="badge badge-danger me-2"></span>
|
<span className="badge badge-danger me-2"></span>
|
||||||
High Priority
|
Ưu tiên
|
||||||
</Option>
|
</Option>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
@ -413,17 +414,17 @@ const CreateProject = () => {
|
|||||||
|
|
||||||
<div className="col-lg-4">
|
<div className="col-lg-4">
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<label className="form-label">Status</label>
|
<label className="form-label">Trạng thái</label>
|
||||||
<Select
|
<Select
|
||||||
value={formData.status}
|
value={formData.status}
|
||||||
onChange={(value) => handleInputChange('status', value)}
|
onChange={(value) => handleInputChange('status', value)}
|
||||||
className="project-select"
|
className="project-select"
|
||||||
>
|
>
|
||||||
<Option value="planning">Planning</Option>
|
<Option value="planning">Dự định</Option>
|
||||||
<Option value="in-progress">In Progress</Option>
|
<Option value="in-progress">Đang thực hiện</Option>
|
||||||
<Option value="review">Review</Option>
|
<Option value="review">Xem xét</Option>
|
||||||
<Option value="completed">Completed</Option>
|
<Option value="completed">Hoàn thành</Option>
|
||||||
<Option value="on-hold">On Hold</Option>
|
<Option value="on-hold">Tạm dừng</Option>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -436,13 +437,13 @@ const CreateProject = () => {
|
|||||||
<div className="form-group-icon">
|
<div className="form-group-icon">
|
||||||
<TrendingUp size={20} />
|
<TrendingUp size={20} />
|
||||||
</div>
|
</div>
|
||||||
<h5>Timeline, Budget & Progress</h5>
|
<h5>Thời gian, Ngân sách & Tiến độ</h5>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-lg-3">
|
<div className="col-lg-3">
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<label className="form-label">Start Date</label>
|
<label className="form-label">Thời gian bắt đầu</label>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
value={formData.startDate}
|
value={formData.startDate}
|
||||||
onChange={(date) => handleInputChange('startDate', date)}
|
onChange={(date) => handleInputChange('startDate', date)}
|
||||||
@ -455,7 +456,7 @@ const CreateProject = () => {
|
|||||||
|
|
||||||
<div className="col-lg-3">
|
<div className="col-lg-3">
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<label className="form-label">End Date</label>
|
<label className="form-label">Thời gian kết thúc</label>
|
||||||
<DatePicker
|
<DatePicker
|
||||||
value={formData.endDate}
|
value={formData.endDate}
|
||||||
onChange={(date) => handleInputChange('endDate', date)}
|
onChange={(date) => handleInputChange('endDate', date)}
|
||||||
@ -469,7 +470,7 @@ const CreateProject = () => {
|
|||||||
|
|
||||||
<div className="col-lg-3">
|
<div className="col-lg-3">
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<label className="form-label">Budget <span className="text-danger">*</span></label>
|
<label className="form-label">Ngân sách <span className="text-danger">*</span></label>
|
||||||
<div className="input-group">
|
<div className="input-group">
|
||||||
<span className="input-group-text">
|
<span className="input-group-text">
|
||||||
<DollarSign size={16} />
|
<DollarSign size={16} />
|
||||||
@ -488,7 +489,7 @@ const CreateProject = () => {
|
|||||||
|
|
||||||
<div className="col-lg-3">
|
<div className="col-lg-3">
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<label className="form-label">Progress Percentage</label>
|
<label className="form-label">Phần trăm tiến độ</label>
|
||||||
<div className="input-group">
|
<div className="input-group">
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
@ -502,7 +503,7 @@ const CreateProject = () => {
|
|||||||
<span className="input-group-text">%</span>
|
<span className="input-group-text">%</span>
|
||||||
</div>
|
</div>
|
||||||
{errors.progressPercentage && <div className="invalid-feedback">{errors.progressPercentage}</div>}
|
{errors.progressPercentage && <div className="invalid-feedback">{errors.progressPercentage}</div>}
|
||||||
<small className="form-text text-muted">Enter progress from 0 to 100</small>
|
<small className="form-text text-muted">Nhập giá trị từ 0 đến 100</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -514,19 +515,19 @@ const CreateProject = () => {
|
|||||||
<div className="form-group-icon">
|
<div className="form-group-icon">
|
||||||
<Users size={20} />
|
<Users size={20} />
|
||||||
</div>
|
</div>
|
||||||
<h5>Team Assignment</h5>
|
<h5>Phân công nhóm</h5>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-lg-6">
|
<div className="col-lg-6">
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<label className="form-label">Project Manager</label>
|
<label className="form-label">Người quản lý</label>
|
||||||
<Select
|
<Select
|
||||||
mode="multiple"
|
mode="multiple"
|
||||||
value={formData.managers}
|
value={formData.managers}
|
||||||
onChange={(value) => handleInputChange('managers', value)}
|
onChange={(value) => handleInputChange('managers', value)}
|
||||||
className="project-select"
|
className="project-select"
|
||||||
placeholder="Select project manager(s) (Optional)"
|
placeholder="Chọn quản lý dự án"
|
||||||
optionLabelProp="label"
|
optionLabelProp="label"
|
||||||
loading={usersLoading}
|
loading={usersLoading}
|
||||||
allowClear
|
allowClear
|
||||||
@ -543,19 +544,19 @@ const CreateProject = () => {
|
|||||||
</Option>
|
</Option>
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
<small className="form-text text-muted">You can assign managers later</small>
|
<small className="form-text text-muted">Bạn có thể chỉ định người quản lý sau</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-lg-6">
|
<div className="col-lg-6">
|
||||||
<div className="mb-3">
|
<div className="mb-3">
|
||||||
<label className="form-label">Team Members</label>
|
<label className="form-label">Thành viên</label>
|
||||||
<Select
|
<Select
|
||||||
mode="multiple"
|
mode="multiple"
|
||||||
value={formData.teamMembers}
|
value={formData.teamMembers}
|
||||||
onChange={(value) => handleInputChange('teamMembers', value)}
|
onChange={(value) => handleInputChange('teamMembers', value)}
|
||||||
className="project-select"
|
className="project-select"
|
||||||
placeholder="Select team members (Optional)"
|
placeholder="Chọn thành viên tham gia dự án"
|
||||||
optionLabelProp="label"
|
optionLabelProp="label"
|
||||||
loading={usersLoading}
|
loading={usersLoading}
|
||||||
allowClear
|
allowClear
|
||||||
@ -572,7 +573,7 @@ const CreateProject = () => {
|
|||||||
</Option>
|
</Option>
|
||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
<small className="form-text text-muted">You can assign team members later</small>
|
<small className="form-text text-muted">Bạn có thể chỉ định sau</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { Table, Progress, Tag, Avatar, Button, DatePicker, Select, Spin } from 'antd';
|
import { Table, Progress, Tag, Avatar, Button, DatePicker, Select, Spin, Modal, message } from 'antd';
|
||||||
import {
|
import {
|
||||||
Star,
|
Star,
|
||||||
Edit,
|
Edit,
|
||||||
@ -162,6 +162,13 @@ const ProjectTracker = () => {
|
|||||||
borderColor: '#f5222d',
|
borderColor: '#f5222d',
|
||||||
textColor: '#f5222d',
|
textColor: '#f5222d',
|
||||||
icon: '⏸️'
|
icon: '⏸️'
|
||||||
|
},
|
||||||
|
'review':{
|
||||||
|
color: '#1890ff',
|
||||||
|
backgroundColor: 'rgba(24, 144, 255, 0.1)',
|
||||||
|
borderColor: '#66a2a3',
|
||||||
|
textColor: '#ffffff',
|
||||||
|
icon: '📋'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -179,6 +186,80 @@ const ProjectTracker = () => {
|
|||||||
return statusMap[status] || status;
|
return statusMap[status] || status;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Delete project function
|
||||||
|
const handleDeleteProject = async (projectId) => {
|
||||||
|
Modal.confirm({
|
||||||
|
title: 'Xác nhận xóa dự án',
|
||||||
|
content: 'Bạn có chắc chắn muốn xóa dự án này không? Hành động này không thể hoàn tác.',
|
||||||
|
okText: 'Xóa',
|
||||||
|
okType: 'danger',
|
||||||
|
cancelText: 'Hủy',
|
||||||
|
onOk: async () => {
|
||||||
|
try {
|
||||||
|
const apiBaseUrl = process.env.REACT_APP_API_BASE_URL || '';
|
||||||
|
console.log('🗑️ Deleting project:', projectId);
|
||||||
|
|
||||||
|
const response = await fetch(`${apiBaseUrl}Projects/delete/${projectId}`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('📡 Delete response status:', response.status);
|
||||||
|
console.log('📡 Delete response ok:', response.ok);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to parse JSON response
|
||||||
|
let result = null;
|
||||||
|
try {
|
||||||
|
const responseText = await response.text();
|
||||||
|
console.log('📄 Raw response text:', responseText);
|
||||||
|
|
||||||
|
if (responseText) {
|
||||||
|
result = JSON.parse(responseText);
|
||||||
|
console.log('✅ Parsed response:', result);
|
||||||
|
} else {
|
||||||
|
console.log('📄 Empty response body');
|
||||||
|
result = { success: true, message: 'Delete successful' };
|
||||||
|
}
|
||||||
|
} catch (parseError) {
|
||||||
|
console.log('⚠️ JSON parse error:', parseError.message);
|
||||||
|
// If JSON parsing fails but HTTP status is OK, consider it success
|
||||||
|
result = { success: true, message: 'Delete successful' };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if response indicates success
|
||||||
|
const isSuccess = response.ok && (
|
||||||
|
!result ||
|
||||||
|
result.success !== false ||
|
||||||
|
result.status !== 'error'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isSuccess) {
|
||||||
|
console.log('✅ Delete operation successful');
|
||||||
|
message.success('Xóa dự án thành công!');
|
||||||
|
|
||||||
|
// Reload projects after successful deletion
|
||||||
|
loadProjects(currentPage, pageSize);
|
||||||
|
} else {
|
||||||
|
console.log('❌ Delete operation failed:', result);
|
||||||
|
const errorMessage = result?.message || result?.error || 'Unknown error occurred';
|
||||||
|
message.error('Xóa dự án thất bại: ' + errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('💥 Error deleting project:', error);
|
||||||
|
message.error('Có lỗi xảy ra khi xóa dự án: ' + error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// Load data on component mount
|
// Load data on component mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
loadProjects();
|
loadProjects();
|
||||||
@ -210,7 +291,6 @@ const ProjectTracker = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Table columns configuration
|
// Table columns configuration
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
@ -242,7 +322,7 @@ const ProjectTracker = () => {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Project Name',
|
title: 'Tên dự án',
|
||||||
dataIndex: 'projectName',
|
dataIndex: 'projectName',
|
||||||
key: 'projectName',
|
key: 'projectName',
|
||||||
render: (text) => (
|
render: (text) => (
|
||||||
@ -261,7 +341,7 @@ const ProjectTracker = () => {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Project Manager',
|
title: 'Quản lý',
|
||||||
dataIndex: 'manager',
|
dataIndex: 'manager',
|
||||||
key: 'manager',
|
key: 'manager',
|
||||||
render: (managers) => (
|
render: (managers) => (
|
||||||
@ -278,7 +358,7 @@ const ProjectTracker = () => {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Start Date',
|
title: 'Bắt đầu',
|
||||||
dataIndex: 'startDate',
|
dataIndex: 'startDate',
|
||||||
key: 'startDate',
|
key: 'startDate',
|
||||||
render: (date) => (
|
render: (date) => (
|
||||||
@ -304,7 +384,7 @@ const ProjectTracker = () => {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Deadline',
|
title: 'Kết thúc',
|
||||||
dataIndex: 'deadline',
|
dataIndex: 'deadline',
|
||||||
key: 'deadline',
|
key: 'deadline',
|
||||||
render: (date) => (
|
render: (date) => (
|
||||||
@ -341,7 +421,7 @@ const ProjectTracker = () => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Budget',
|
title: 'Ngân sách',
|
||||||
dataIndex: 'budget',
|
dataIndex: 'budget',
|
||||||
key: 'budget',
|
key: 'budget',
|
||||||
render: (budget) => (
|
render: (budget) => (
|
||||||
@ -352,11 +432,22 @@ const ProjectTracker = () => {
|
|||||||
title: '',
|
title: '',
|
||||||
key: 'actions',
|
key: 'actions',
|
||||||
width: 100,
|
width: 100,
|
||||||
render: () => (
|
render: (_, record) => (
|
||||||
<div className="action-table-data">
|
<div className="action-table-data">
|
||||||
<div className="edit-delete-action">
|
<div className="edit-delete-action">
|
||||||
<Edit size={16} style={{ cursor: 'pointer' }} />
|
<Edit
|
||||||
<Trash2 size={16} style={{ cursor: 'pointer' }} />
|
size={16}
|
||||||
|
style={{ cursor: 'pointer', color: '#1890ff', marginRight: '8px' }}
|
||||||
|
onClick={() => {
|
||||||
|
// TODO: Navigate to edit page
|
||||||
|
message.info('Edit functionality will be implemented');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Trash2
|
||||||
|
size={16}
|
||||||
|
style={{ cursor: 'pointer', color: '#ff4d4f' }}
|
||||||
|
onClick={() => handleDeleteProject(record.id || record.key)}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@ -381,8 +472,8 @@ const ProjectTracker = () => {
|
|||||||
<div className="page-header">
|
<div className="page-header">
|
||||||
<div className="add-item d-flex">
|
<div className="add-item d-flex">
|
||||||
<div className="page-title">
|
<div className="page-title">
|
||||||
<h4>Project Tracker</h4>
|
<h4>Theo dõi tiến độ dự án</h4>
|
||||||
<h6>Manage Your Projects</h6>
|
<h6>Quản lý dự án</h6>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="page-btn">
|
<div className="page-btn">
|
||||||
@ -392,7 +483,7 @@ const ProjectTracker = () => {
|
|||||||
icon={<Plus size={16} />}
|
icon={<Plus size={16} />}
|
||||||
className="btn btn-added"
|
className="btn btn-added"
|
||||||
>
|
>
|
||||||
Create New Project
|
Thêm mới
|
||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
@ -405,7 +496,7 @@ const ProjectTracker = () => {
|
|||||||
<div className="search-set">
|
<div className="search-set">
|
||||||
<div className="search-input">
|
<div className="search-input">
|
||||||
<span style={{ fontSize: '16px', fontWeight: '500' }}>
|
<span style={{ fontSize: '16px', fontWeight: '500' }}>
|
||||||
Project Lists
|
Danh sách dự án
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@ -455,12 +546,12 @@ const ProjectTracker = () => {
|
|||||||
className="project-filter-select"
|
className="project-filter-select"
|
||||||
style={{ width: 140, height: 42 }}
|
style={{ width: 140, height: 42 }}
|
||||||
>
|
>
|
||||||
<Option value="All Status">Select Status</Option>
|
<Option value="All Status">Chọn trạng thái</Option>
|
||||||
<Option value="Planning">📋 Planning</Option>
|
<Option value="Planning">📋 Dự định</Option>
|
||||||
<Option value="Completed">✅ Completed</Option>
|
<Option value="Completed">✅ Hoàn thành</Option>
|
||||||
<Option value="Pending">⏳ Pending</Option>
|
<Option value="Pending">⏳ Pending</Option>
|
||||||
<Option value="Inprogress">🚀 In Progress</Option>
|
<Option value="Inprogress">🚀 Hiện thực</Option>
|
||||||
<Option value="Onhold">⏸️ On Hold</Option>
|
<Option value="Onhold">⏸️ Tạm dừng</Option>
|
||||||
</Select>
|
</Select>
|
||||||
|
|
||||||
<Select
|
<Select
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user