diff --git a/content/content_api_handler.go b/content/content_api_handler.go index 97a71bf..9932d8d 100644 --- a/content/content_api_handler.go +++ b/content/content_api_handler.go @@ -103,6 +103,7 @@ func ContentDeleteAPIHandler(w http.ResponseWriter, req *http.Request) { func ContentCreateAPIHandler(w http.ResponseWriter, req *http.Request) { var contentPayload ContentPayload _ = json.NewDecoder(req.Body).Decode(&contentPayload) + data := createContent(contentPayload) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) @@ -114,10 +115,10 @@ func ContentRenameAPIHandler(w http.ResponseWriter, req *http.Request) { var renameContentPayload RenameContentPayload _ = json.NewDecoder(req.Body).Decode(&renameContentPayload) - oldPath := renameContentPayload.OldPath - log.Info().Msgf("old path : %s", oldPath) + oldName := renameContentPayload.OldName + log.Info().Msgf("old path : %s", oldName) - rename(oldPath, renameContentPayload.Path) + rename(renameContentPayload.ParentDir, oldName, renameContentPayload.NewName) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) diff --git a/content/content_manager.go b/content/content_manager.go index 4a2b9b9..6d13362 100644 --- a/content/content_manager.go +++ b/content/content_manager.go @@ -200,19 +200,19 @@ func createContent(payload ContentPayload) models.ContentModel { if payload.Extension == ".ipynb" { model.ContentType = "notebook" filename := "untitled.ipynb" - model.Path = GetOSPath(filename) + model.Path = GetOSPath(payload.ParentDir + "/" + filename) model.Name = filename newUntitledFile(model) } else if payload.ContentType == "directory" { model.ContentType = "directory" filename := "untitled_directory" - model.Path = GetOSPath(filename) + model.Path = GetOSPath(payload.ParentDir + "/" + filename) model.Name = filename CreateDirectory(model.Path) } else { model.ContentType = "file" filename := "untitled.txt" - model.Path = GetOSPath(filename) + model.Path = GetOSPath(payload.ParentDir + "/" + filename) model.Name = filename newUntitledNotebook(model) } @@ -260,10 +260,10 @@ func CreateDirectory(dirPath string) error { return nil } -func rename(oldName, newName string) error { - err := os.Rename(GetOSPath(oldName), GetOSPath(newName)) +func rename(parentDir, oldName, newName string) error { + err := os.Rename(GetOSPath(parentDir+"/"+oldName), GetOSPath(parentDir+"/"+newName)) if err != nil { - return err + log.Info().Msgf("error is %s", err) } return nil } diff --git a/content/payload.go b/content/payload.go index 5b15a74..ba417e2 100644 --- a/content/payload.go +++ b/content/payload.go @@ -8,11 +8,13 @@ type ( ContentPayload struct { Extension string `json:"ext"` ContentType string `json:"type"` + ParentDir string `json:"parent_dir"` } RenameContentPayload struct { - OldPath string `json:"old_path"` - Path string `json:"path"` + ParentDir string `json:"parent_dir"` + OldName string `json:"old_name"` + NewName string `json:"new_name"` } ContentUpdateRequest struct { diff --git a/ui/src/ide/sidebar/ContextMenu.scss b/ui/src/ide/sidebar/ContextMenu.scss index 09a126f..a9f8a99 100644 --- a/ui/src/ide/sidebar/ContextMenu.scss +++ b/ui/src/ide/sidebar/ContextMenu.scss @@ -5,12 +5,16 @@ border: 1px solid #ccc; box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2); z-index: 1000; - width: 150px; + width: 12 0px; + border-radius: 5px; } .context-menu-item { - padding: 8px 12px; + padding: 4px 12px; cursor: pointer; + color: #473990; + font-size: 12px; + font-weight: 400; } .context-menu-item:hover { diff --git a/ui/src/ide/sidebar/FileBrowser.tsx b/ui/src/ide/sidebar/FileBrowser.tsx index 007cda9..2fd89db 100644 --- a/ui/src/ide/sidebar/FileBrowser.tsx +++ b/ui/src/ide/sidebar/FileBrowser.tsx @@ -4,9 +4,11 @@ import ContextMenu from './ContextMenu'; import getFileExtension from '../utils'; import { useAtom } from 'jotai'; import { userNameAtom } from '../../store/AppState'; +import { v4 as uuidv4 } from 'uuid'; interface IContent { + id: string; type: string; path: string; name: string; @@ -30,13 +32,16 @@ export default function FileBrowser({ sendDataToParent, display }: FileBrowserPr body: JSON.stringify({ path: cwd }), }); const resJson = await res.json(); + resJson.content.forEach((item) => { + item.id = uuidv4(); + }); setContents(resJson.content); const res2 = await fetch(BaseApiUrl + '/api/info'); const resJson2 = await res2.json(); setProjectName(resJson2.project.toUpperCase()); setUserName(resJson2.username) - + }; const handleFileClick = (name: string, path: string, type: string) => { @@ -46,7 +51,7 @@ export default function FileBrowser({ sendDataToParent, display }: FileBrowserPr const createNewFile = async () => { await fetch(BaseApiUrl + '/api/contents/create', { method: 'POST', - body: JSON.stringify({ ext: '.py', type: 'file' }), + body: JSON.stringify({ parent_dir: "", ext: '.py', type: 'file' }), }); FetchData(); }; @@ -88,13 +93,14 @@ export default function FileBrowser({ sendDataToParent, display }: FileBrowserPr {contents.map((content, index) => ( content.type === 'directory' ? ( ) : ( @@ -107,37 +113,13 @@ export default function FileBrowser({ sendDataToParent, display }: FileBrowserPr ); } -const FileItem = ({ content, handleFileClick }: { content: IContent; - handleFileClick: (name: string, path: string, type: string) => void , - }) => { - const [isEditing, setIsEditing] = useState(false); - const [text, setText] = useState(content.name); - const [menuPosition, setMenuPosition] = useState<{ xPos: number; yPos: number } | null>(null); - const [isMenuVisible, setIsMenuVisible] = useState(false); - - const renameContent = async () => { - setIsEditing(false) - await fetch(BaseApiUrl + '/api/contents/rename', { - method: 'POST', - body: JSON.stringify({ old_path: content.name, new_path: text }), - }); - }; - - const menuItems = [ - { - label: 'Rename', - action: (path: string) => { - // e.stopPropagation(); // Stop the click event from propagating - setIsEditing(true); - - } - }, - // { label: 'Delete', action: async () => await deleteFile(data.path) } - ]; - - - const getIconToLoad = () => { - const extension = getFileExtension(content.name); +const FileItem = ( + { parentDir, content, handleFileClick }: { + parentDir: string; content: IContent; + handleFileClick: (name: string, path: string, type: string) => void, + }) => { + const getIconToLoad = (fileName) => { + const extension = getFileExtension(fileName); const iconMap: { [key: string]: string } = { go: './images/editor/go-icon.svg', mod: './images/editor/go-icon.svg', @@ -161,6 +143,52 @@ const FileItem = ({ content, handleFileClick }: { content: IContent; }; return extension != null ? iconMap[extension] : './images/editor/go-icon.svg'; }; + const [isEditing, setIsEditing] = useState(false); + const [contentName, setContentName] = useState(content.name); + const [text, setText] = useState(content.name); + const [menuPosition, setMenuPosition] = useState<{ xPos: number; yPos: number } | null>(null); + const [isMenuVisible, setIsMenuVisible] = useState(false); + const [icon, setIcon] = useState(getIconToLoad(content.name)) + const [isDeleted, setIsDeleted] = useState(false) + + const renameContent = async () => { + setIsEditing(false) + await fetch(BaseApiUrl + '/api/contents/rename', { + method: 'POST', + body: JSON.stringify({ parent_dir: parentDir, old_name: contentName, new_name: text }), + }); + setContentName(text) + setIcon(getIconToLoad(text)) + }; + + const deleteContent = async () => { + await fetch(BaseApiUrl + '/api/contents', { + method: 'DELETE', + body: JSON.stringify({ path: getPath() }), + }); + setIsDeleted(true) + }; + + const menuItems = [ + { + label: 'Rename', + action: (path: string) => { + // e.stopPropagation(); // Stop the click event from propagating + setIsEditing(true); + } + }, + { + label: 'Delete', + action: (path: string) => { + // e.stopPropagation(); // Stop the click event from propagating + deleteContent() + + } + }, + ]; + + + const handleRightClick = (e: React.MouseEvent, path: string) => { e.preventDefault(); @@ -168,19 +196,31 @@ const FileItem = ({ content, handleFileClick }: { content: IContent; setIsMenuVisible(true); }; - const handleClick = (name: string, path: string, type:string) => { + const getPath = () =>{ + if (parentDir === ""){ + return text + }else{ + return parentDir + "/" + text + } + } + + const handleClick = (name: string, path: string, type: string) => { if (!isMenuVisible) { - handleFileClick(name, path, type) + handleFileClick(name, getPath(), type) } } + if (isDeleted){ + return <> + } + return (
  • handleClick(content.name, content.path, content.type)} + onClick={() => handleClick(text, content.path, content.type)} onContextMenu={(e) => handleRightClick(e, content.path)} > - + {isEditing ? ( {text} )} {isMenuVisible && menuPosition && ( - setIsMenuVisible(false)} - /> - )} + setIsMenuVisible(false)} + /> + )}
  • ); }; -const DirectoryItem = ({data, sendDataToParent }) => { +const DirectoryItem = ({ data, sendDataToParent }) => { const [isEditing, setIsEditing] = useState(false); const [content, setContent] = useState(data) const [text, setText] = useState(data.name); @@ -223,12 +263,35 @@ const DirectoryItem = ({data, sendDataToParent }) => { body: JSON.stringify({ path }), }); const resJson = await res.json(); + resJson.content.forEach((item) => { + item.id = uuidv4(); + }); + setContent(resJson) + }; + + const createNewFile = async (path: string) => { + console.log("add file") + await fetch(BaseApiUrl + '/api/contents/create', { + method: 'POST', + body: JSON.stringify({ parent_dir: path, ext: '.py', type: 'file' }), + }); + + const res = await fetch(BaseApiUrl + '/api/contents?type=notebook&hash=0', { + method: 'POST', + body: JSON.stringify({ path }), + }); + const resJson = await res.json(); + resJson.content.forEach((item) => { + item.id = uuidv4(); + }); setContent(resJson) }; const menuItems = [ { label: 'Rename', action: () => setIsEditing(true) }, - // { label: 'Delete', action: async () => await deleteFile(data.path) } + { label: 'Add file', action: (path: string) => createNewFile(path) }, + { label: 'Add Notebook', action: (path: string) => console.log("add notebook") }, + { label: 'Add directory', action: (path: string) => console.log("add directory" + path) } ]; const handleRightClick = (e: React.MouseEvent, path: string) => { @@ -263,17 +326,17 @@ const DirectoryItem = ({data, sendDataToParent }) => { onClose={() => setIsMenuVisible(false)} /> )} - + ); };