Add git api calls on frontend

This commit is contained in:
2024-09-28 19:39:45 +02:00
parent 4290119b93
commit 5af07a9e35
6 changed files with 317 additions and 87 deletions

View File

@@ -1,22 +1,28 @@
import React from 'react';
import { Tree } from '@geist-ui/core';
import { File, Folder } from '@geist-ui/icons';
import { Tree, Button, Tooltip, Spacer, ButtonGroup } from '@geist-ui/core';
import { File, Folder, GitPullRequest, GitCommit, Plus, Trash } from '@geist-ui/icons';
const FileTree = ({
files = [],
onFileSelect = () => {},
selectedFile = null
selectedFile = null,
gitEnabled = false,
gitAutoCommit = false,
onPull = () => {},
onCommitAndPush = () => {},
onCreateFile = () => {},
onDeleteFile = () => {}
}) => {
if (files.length === 0) {
return <div>No files to display</div>;
}
const handleSelect = (filePath) => {
onFileSelect(filePath);
onFileSelect(filePath);
};
const renderLabel = (node) => {
const path = getFilePath(node);
const path = node.extra;
return (
<span style={{ color: path === selectedFile ? '#0070f3' : 'inherit' }}>
{node.name}
@@ -27,12 +33,63 @@ const FileTree = ({
const renderIcon = ({ type }) => type === 'directory' ? <Folder /> : <File />;
return (
<Tree
value={files}
onClick={handleSelect}
renderIcon={renderIcon}
renderLabel={renderLabel}
/>
<div>
<ButtonGroup className='file-tree-buttons'>
<Tooltip text="Create new file" type="dark">
<Button
icon={<Plus />}
auto
scale={2/3}
onClick={onCreateFile}
px={0.6}
/>
</Tooltip>
<Spacer w={0.5} />
<Tooltip text={selectedFile ? "Delete current file" : "No file selected"} type="dark">
<Button
icon={<Trash />}
auto
scale={2/3}
onClick={onDeleteFile}
disabled={!selectedFile}
type="error"
px={0.6}
/>
</Tooltip>
<Spacer w={0.5} />
<Tooltip text={gitEnabled ? "Pull changes from remote" : "Git is not enabled"} type="dark">
<Button
icon={<GitPullRequest />}
auto
scale={2/3}
onClick={onPull}
disabled={!gitEnabled}
px={0.6}
/>
</Tooltip>
<Spacer w={0.5} />
<Tooltip text={
!gitEnabled ? "Git is not enabled" :
gitAutoCommit ? "Auto-commit is enabled" :
"Commit and push changes"
} type="dark">
<Button
icon={<GitCommit />}
auto
scale={2/3}
onClick={onCommitAndPush}
disabled={!gitEnabled || gitAutoCommit}
px={0.6}
/>
</Tooltip>
</ButtonGroup>
<Tree
value={files}
onClick={handleSelect}
renderIcon={renderIcon}
renderLabel={renderLabel}
/>
</div>
);
};

View File

@@ -1,9 +1,10 @@
import React, { useState } from 'react';
import { Grid, Breadcrumbs, Tabs, Dot, useTheme } from '@geist-ui/core';
import { Grid, Breadcrumbs, Tabs, Dot, useTheme, useToasts, Modal, Input, Button } from '@geist-ui/core';
import { Code, Eye } from '@geist-ui/icons';
import Editor from './Editor';
import FileTree from './FileTree';
import MarkdownPreview from './MarkdownPreview';
import { commitAndPush, saveFileContent, deleteFile } from '../services/api';
const MainContent = ({
content,
@@ -14,9 +15,71 @@ const MainContent = ({
onFileSelect,
onContentChange,
onSave,
settings,
pullLatestChanges,
}) => {
const [activeTab, setActiveTab] = useState('source');
const { type: themeType } = useTheme();
const { setToast } = useToasts();
const [newFileModalVisible, setNewFileModalVisible] = useState(false);
const [newFileName, setNewFileName] = useState('');
const handlePull = async () => {
try {
await pullLatestChanges();
setToast({ text: 'Successfully pulled latest changes', type: 'success' });
} catch (error) {
setToast({ text: 'Failed to pull changes: ' + error.message, type: 'error' });
}
};
const handleCommitAndPush = async () => {
try {
const message = prompt('Enter commit message:');
if (message) {
await commitAndPush(message);
setToast({ text: 'Changes committed and pushed successfully', type: 'success' });
await pullLatestChanges(); // Pull changes after successful push
}
} catch (error) {
setToast({ text: 'Failed to commit and push changes: ' + error.message, type: 'error' });
}
};
const handleCreateFile = () => {
setNewFileModalVisible(true);
};
const handleNewFileSubmit = async () => {
if (newFileName) {
try {
await saveFileContent(newFileName, '');
setToast({ text: 'New file created successfully', type: 'success' });
await pullLatestChanges(); // Refresh file list
onFileSelect(newFileName); // Select the new file
} catch (error) {
setToast({ text: 'Failed to create new file: ' + error.message, type: 'error' });
}
}
setNewFileModalVisible(false);
setNewFileName('');
};
const handleDeleteFile = async () => {
if (selectedFile) {
const confirmDelete = window.confirm(`Are you sure you want to delete "${selectedFile}"?`);
if (confirmDelete) {
try {
await deleteFile(selectedFile);
setToast({ text: 'File deleted successfully', type: 'success' });
await pullLatestChanges(); // Refresh file list
onFileSelect(null); // Deselect the file
} catch (error) {
setToast({ text: 'Failed to delete file: ' + error.message, type: 'error' });
}
}
}
};
const renderBreadcrumbs = () => {
if (!selectedFile) return null;
@@ -34,41 +97,60 @@ const MainContent = ({
};
return (
<Grid.Container gap={1} height="calc(100vh - 64px)">
<Grid xs={24} sm={6} md={5} lg={4} height="100%" className="sidebar">
{error ? (
<div className="error">{error}</div>
) : (
<FileTree
files={files}
onFileSelect={onFileSelect}
selectedFile={selectedFile}
/>
)}
</Grid>
<Grid xs={24} sm={18} md={19} lg={20} height="100%" className="main-content">
<div className="content-header">
{renderBreadcrumbs()}
<Tabs value={activeTab} onChange={setActiveTab}>
<Tabs.Item label={<Code />} value="source" />
<Tabs.Item label={<Eye />} value="preview" />
</Tabs>
</div>
<div className="content-body">
{activeTab === 'source' ? (
<Editor
content={content}
onChange={onContentChange}
onSave={onSave}
filePath={selectedFile}
themeType={themeType}
<>
<Grid.Container gap={1} height="calc(100vh - 64px)">
<Grid xs={24} sm={6} md={5} lg={4} height="100%" className="sidebar">
<div className="file-tree-container">
<FileTree
files={files}
onFileSelect={onFileSelect}
selectedFile={selectedFile}
gitEnabled={settings.gitEnabled}
gitAutoCommit={settings.gitAutoCommit}
onPull={handlePull}
onCommitAndPush={handleCommitAndPush}
onCreateFile={handleCreateFile}
onDeleteFile={handleDeleteFile}
/>
) : (
<MarkdownPreview content={content} />
)}
</div>
</Grid>
</Grid.Container>
</div>
</Grid>
<Grid xs={24} sm={18} md={19} lg={20} height="100%" className="main-content">
<div className="content-header">
{renderBreadcrumbs()}
<Tabs value={activeTab} onChange={setActiveTab}>
<Tabs.Item label={<Code />} value="source" />
<Tabs.Item label={<Eye />} value="preview" />
</Tabs>
</div>
<div className="content-body">
{activeTab === 'source' ? (
<Editor
content={content}
onChange={onContentChange}
onSave={onSave}
filePath={selectedFile}
themeType={themeType}
/>
) : (
<MarkdownPreview content={content} />
)}
</div>
</Grid>
</Grid.Container>
<Modal visible={newFileModalVisible} onClose={() => setNewFileModalVisible(false)}>
<Modal.Title>Create New File</Modal.Title>
<Modal.Content>
<Input
width="100%"
placeholder="Enter file name"
value={newFileName}
onChange={(e) => setNewFileName(e.target.value)}
/>
</Modal.Content>
<Modal.Action passive onClick={() => setNewFileModalVisible(false)}>Cancel</Modal.Action>
<Modal.Action onClick={handleNewFileSubmit}>Create</Modal.Action>
</Modal>
</>
);
};