diff --git a/src/components/PCItem.jsx b/src/components/PCItem.jsx new file mode 100644 index 0000000000000000000000000000000000000000..e56ae366ce00f1977a81aab30843c9f89108550c --- /dev/null +++ b/src/components/PCItem.jsx @@ -0,0 +1,111 @@ +import React, { useState } from 'react'; + +const PCItem = ({ pc, isSelected, onPcClick, onEdit, onDelete }) => { + const [isEditing, setIsEditing] = useState(false); + const [editName, setEditName] = useState(pc.name); + const [isLoading, setIsLoading] = useState(false); + const [isDeleting, setIsDeleting] = useState(false); + const [error, setError] = useState(null); + + const handleEditClick = (e) => { + e.stopPropagation(); + setIsEditing(true); + setEditName(pc.name); + setError(null); + }; + + const handleCancel = (e) => { + e.stopPropagation(); + setIsEditing(false); + setError(null); + }; + + const handleSubmit = async (e) => { + e.stopPropagation(); + if (!editName.trim()) { + setError("PC 이름을 입력해주세요"); + return; + } + + setIsLoading(true); + try { + await onEdit(pc.id, editName); + setIsEditing(false); + } catch (error) { + setError("PC 이름 변경 중 오류가 발생했습니다"); + } finally { + setIsLoading(false); + } + }; + + const handleDelete = async (e) => { + e.stopPropagation(); + if (!window.confirm('정말로 이 PC를 삭제하시겠습니까?')) { + return; + } + + setIsDeleting(true); + try { + await onDelete(pc.id); + } catch (error) { + console.error("PC 삭제 중 오류 발생:", error); + } finally { + setIsDeleting(false); + } + }; + + return ( + <li + className={`pc-item ${isSelected ? 'active' : ''}`} + > + {isEditing ? ( + <div className="edit-name-container" onClick={e => e.stopPropagation()}> + <input + type="text" + value={editName} + onChange={(e) => setEditName(e.target.value)} + placeholder="PC 이름 입력" + /> + <div className="edit-buttons"> + <button + onClick={handleSubmit} + disabled={isLoading} + > + {isLoading ? '저장 중...' : '저장'} + </button> + <button + onClick={handleCancel} + className="cancel-button" + disabled={isLoading} + > + 취소 + </button> + </div> + {error && <div className="error-message">{error}</div>} + </div> + ) : ( + <div className="pc-item-content" onClick={() => onPcClick(pc.id)}> + <span>{pc.name}</span> + <div className="pc-item-buttons"> + <button + className="edit-button" + onClick={handleEditClick} + disabled={isDeleting} + > + 수정 + </button> + <button + className="delete-button" + onClick={handleDelete} + disabled={isDeleting} + > + {isDeleting ? '삭제 중...' : '삭제'} + </button> + </div> + </div> + )} + </li> + ); +}; + +export default PCItem; \ No newline at end of file diff --git a/src/components/PCList.jsx b/src/components/PCList.jsx index 833cf07d8f95847d60d49291763d6afb89b3b77f..3947bd93960ca18c934ddff24b6c900de481cc1e 100644 --- a/src/components/PCList.jsx +++ b/src/components/PCList.jsx @@ -1,27 +1,35 @@ import React from "react"; -import { Link } from "react-router-dom"; +import PCItem from "./PCItem"; -const PCList = ({ pcs, selectedPc, onPcClick }) => ( - <aside className="sidebar"> - <h3>내 PC 목록</h3> - <ul> - {pcs.map((pc) => ( - <li - key={pc.id} - onClick={() => onPcClick(pc.id)} - className={`pc-item ${selectedPc && selectedPc.id === pc.id ? 'active' : ''}`} - > - {pc.name} - </li> - ))} - </ul> - <button className="add-pc-btn"> - <Link to="/partscertification" className="add-pc-btn-link"> +const PCList = ({ + pcs, + selectedPc, + onPcClick, + onEditPC, + onDeletePC, + onAddPC +}) => { + return ( + <aside className="sidebar"> + <h3>내 PC 목록</h3> + <ul> + {pcs.map((pc) => ( + <PCItem + key={pc.id} + pc={pc} + isSelected={selectedPc && selectedPc.id === pc.id} + onPcClick={onPcClick} + onEdit={onEditPC} + onDelete={onDeletePC} + /> + ))} + </ul> + <button className="add-pc-btn" onClick={onAddPC}> <span>+</span> <span>새 PC 등록하기</span> - </Link> - </button> - </aside> -); + </button> + </aside> + ); +}; export default PCList; \ No newline at end of file diff --git a/src/pages/MyCombinationPage/MyCombinationPage.jsx b/src/pages/MyCombinationPage/MyCombinationPage.jsx index 066e674cdd61d90c9affdddefe1cf2de519fb1d3..b6507f7942de453cf22503c07cc607a74dd04eb9 100644 --- a/src/pages/MyCombinationPage/MyCombinationPage.jsx +++ b/src/pages/MyCombinationPage/MyCombinationPage.jsx @@ -6,18 +6,14 @@ import './MyCombinationPage.css'; import PartItem from "@/components/PartItem"; import updatePCName from "@/api/my/updatePCName"; import deletePC from "@/api/my/deletePC"; +import PCList from "@/components/PCList"; const CertifiedCombination = () => { const [pcs, setPcs] = useState([]); const [selectedPc, setSelectedPc] = useState(null); const [partsData, setPartsData] = useState([]); - const [isLoading, setIsLoading] = useState(false); - const [error, setError] = useState(null); - const [editingPcId, setEditingPcId] = useState(null); - const [editingName, setEditingName] = useState(""); - const navigate = useNavigate(); const [isPartsLoading, setIsPartsLoading] = useState(false); - const [isDeleting, setIsDeleting] = useState(false); + const navigate = useNavigate(); useEffect(() => { const fetchPCs = async () => { @@ -71,52 +67,19 @@ const CertifiedCombination = () => { navigate('/partscertification'); }; - const handleEditClick = (pc) => { - setError(null); - setEditingPcId(pc.id); - setEditingName(pc.name); - }; - - const handleCancel = () => { - setEditingPcId(null); - setError(null); - }; - - const handleNameSubmit = async (pcId) => { - if (!editingName.trim()) { - setError("PC 이름을 입력해주세요"); - return; - } - - setIsLoading(true); - setError(null); - + const handleEditPC = async (pcId, newName) => { try { - await updatePCName(pcId, editingName); + await updatePCName(pcId, newName); setPcs(pcs.map(pc => - pc.id === pcId ? { ...pc, name: editingName } : pc + pc.id === pcId ? { ...pc, name: newName } : pc )); - setEditingPcId(null); } catch (error) { - setError("PC 이름 변경 중 오류가 발생했습니다"); console.error("PC 이름 변경 중 오류 발생:", error); - } finally { - setIsLoading(false); + throw error; } }; - const handleNameChange = (e) => { - setEditingName(e.target.value); - }; - - const handleDeleteClick = async (pcId, e) => { - e.stopPropagation(); - - if (!window.confirm('정말로 이 PC를 삭제하시겠습니까?')) { - return; - } - - setIsDeleting(true); + const handleDeletePC = async (pcId) => { try { await deletePC(pcId); setPcs(pcs.filter(pc => pc.id !== pcId)); @@ -126,85 +89,19 @@ const CertifiedCombination = () => { } catch (error) { console.error("PC 삭제 중 오류 발생:", error); alert("PC 삭제 중 오류가 발생했습니다"); - } finally { - setIsDeleting(false); } }; return ( <div className="layout"> - <aside className="sidebar"> - <h3>내 PC 목록</h3> - <ul> - {pcs.map((pc) => ( - <li - key={pc.id} - className={`pc-item ${selectedPc && selectedPc.id === pc.id ? 'active' : ''}`} - > - {editingPcId === pc.id ? ( - <div className="edit-name-container"> - <input - type="text" - value={editingName} - onChange={handleNameChange} - onClick={(e) => e.stopPropagation()} - placeholder="PC 이름 입력" - /> - <div className="edit-buttons"> - <button - onClick={(e) => { - e.stopPropagation(); - handleNameSubmit(pc.id); - }} - disabled={isLoading} - > - {isLoading ? '저장 중...' : '저장'} - </button> - <button - onClick={(e) => { - e.stopPropagation(); - handleCancel(); - }} - className="cancel-button" - disabled={isLoading} - > - 취소 - </button> - </div> - {error && <div className="error-message">{error}</div>} - </div> - ) : ( - <div className="pc-item-content" onClick={() => handlePcClick(pc.id)}> - <span>{pc.name}</span> - <div className="pc-item-buttons"> - <button - className="edit-button" - onClick={(e) => { - e.stopPropagation(); - handleEditClick(pc); - }} - > - 수정 - </button> - <button - className="delete-button" - onClick={(e) => handleDeleteClick(pc.id, e)} - disabled={isDeleting} - > - {isDeleting ? '삭제 중...' : '삭제'} - </button> - </div> - </div> - )} - </li> - ))} - </ul> - <button className="add-pc-btn" onClick={handleAddPcClick}> - <span>+</span> - <span>새 PC 등록하기</span> - </button> - </aside> - + <PCList + pcs={pcs} + selectedPc={selectedPc} + onPcClick={handlePcClick} + onEditPC={handleEditPC} + onDeletePC={handleDeletePC} + onAddPC={handleAddPcClick} + /> <main className={`part-list ${isPartsLoading ? 'loading' : ''}`}> {partsData.map((part, index) => ( <PartItem key={index} part={part} />