Skip to content

Commit

Permalink
feat: file upload and display section functionallity
Browse files Browse the repository at this point in the history
  • Loading branch information
PauliusPreiksaCode committed Jun 24, 2024
1 parent 008eb77 commit 5252473
Show file tree
Hide file tree
Showing 13 changed files with 406 additions and 12 deletions.
132 changes: 132 additions & 0 deletions GUI/front-end/src/components/displays/extension/FileInstance.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { Box, Typography, useTheme, IconButton, Button } from "@mui/material";
import { useLanguageContext } from "../../../contexts";
import { useEffect, useState } from "react";
import { useDeleteFile, useGetWorkspaceFiles, useUploadFile } from "../../../hooks";
import { useWorkspaceContext } from "../../../contexts/tool/UseWorkspaceContext";
import DeleteForeverIcon from '@mui/icons-material/DeleteForever';
import FileInput from "../../fileInput/FileInput";

const FileInstance: React.FC = () => {

const Theme = useTheme();

const languageContext = useLanguageContext();
const workspaceContext = useWorkspaceContext();
const deleteFile = useDeleteFile();
const createFile = useUploadFile();


const [files, setFiles] = useState<string[]>([]);
const [file, setFile] = useState<string | ArrayBuffer | null>(null);
const [fileName, setFileName] = useState<string>('');

const { data, isLoading, isFetching } = useGetWorkspaceFiles(workspaceContext.workspace as string);

useEffect(() => {
if (data?.files.length > 0) {
setFiles(data.files);
} else {
setFiles([]);
}
}, [data]);

const onFileSelect = (selectedFile: string | ArrayBuffer | null, name: string) => {
setFile(selectedFile);
setFileName(name);
}

const createNewFile = async () => {

const binaryData = dataURLtoBlob(file);

const data = new FormData();
data.append('workspace', workspaceContext.workspace as string);
data.append('file', binaryData);
data.append('file_name', fileName);

await createFile.mutateAsync(data);
}

const dataURLtoBlob = (file : string | ArrayBuffer | null) => {
const stringFile = file as string;
const arr = stringFile.split(",");
const mimeMatch = arr[0].match(/:(.*?);/);

if (!mimeMatch) {
return new Blob();
}

const mime = mimeMatch[1];
const bstr = atob(arr[1]);
let n = bstr.length;
const u8arr = new Uint8Array(n);

while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}

return new Blob([u8arr], { type: mime });
};

return (
<>
{workspaceContext.workspace && !isLoading && !isFetching && (
<>
<Typography>{languageContext.language === 'en' ? 'Files' : 'Failai'}</Typography>
<Box
sx={{
color: Theme.palette.text.secondary,
backgroundColor: Theme.palette.background.default,
borderRadius: '10px',
padding: '10px',
px: '10px',
}}
>
<FileInput onFileSelect={onFileSelect} />
<Button
variant="contained"
sx={{
color: Theme.palette.text.secondary,
backgroundColor: Theme.palette.background.default,
borderRadius: '10px',
height: '40px',
px: '10px',
}}
onClick={() => createNewFile()}
>
{languageContext.language === 'en' ? 'Upload file' : 'Įkelti failą'}
</Button>
{file && fileName && (fileName)}
{
files?.length > 0 ? (
files.map((file) => (
<Typography key={file}>
{file}
<IconButton
edge="end"
aria-label="delete"
size="small"
onClick={ async () => {
const data = {
workspace: workspaceContext.workspace,
file_name: file,
}

await deleteFile.mutateAsync(data);
}}
>
<DeleteForeverIcon sx={{ height: '40px' }} />
</IconButton>
</Typography>
))
) : (
<Typography>{languageContext.language === 'en' ? 'No files' : 'Nėra failų'}</Typography>
)}
</Box>
</>
)}
</>
);
}

export default FileInstance;
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { ArrowDropDown as ArrowDropDownIcon } from '@mui/icons-material/';
import { Box, MenuItem, Select, Typography, styled, useTheme } from '@mui/material';
import { useLanguageContext, useToolContext } from '../../../contexts';
import React from 'react';
import WorkspaceInstance from './WorkspaceInstance';

interface Props {}

Expand Down Expand Up @@ -44,6 +45,8 @@ const FunctionalityInstance: React.FC<Props> = () => {
</MenuItem>
))}
</Select>
<Typography>{languageContext.language === 'en' ? 'Workspace' : 'Darbo aplinka'}</Typography>
<WorkspaceInstance />
</Box>
);
};
Expand Down
115 changes: 115 additions & 0 deletions GUI/front-end/src/components/displays/extension/WorkspaceInstance.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { Select, useTheme, styled, MenuItem, TextField, Button, IconButton } from "@mui/material";
import { ArrowDropDown as ArrowDropDownIcon } from '@mui/icons-material/';
import { useLanguageContext } from "../../../contexts";
import { useWorkspaceContext } from "../../../contexts/tool/UseWorkspaceContext";
import { useEffect, useState } from "react";
import { useCreateWorkspace, useDeleteWorkspace, useGetWorkspaces } from "../../../hooks";
import DeleteForeverIcon from '@mui/icons-material/DeleteForever';
import FileInstance from "./FileInstance";

const WorkspaceInstance: React.FC = () => {
const Theme = useTheme();

const languageContext = useLanguageContext();
const workspaceContext = useWorkspaceContext();

const [awailableWorkspaces, setAwailableWorkspaces] = useState<string[]>([]);
const [newWorkspace, setNewWorkspace] = useState<string>('');

const { data, isLoading, isFetching } = useGetWorkspaces();
const createWorkspace = useCreateWorkspace();
const deleteWorkspace = useDeleteWorkspace();

const createNewWorkspace = () => {
createWorkspace.mutate(newWorkspace);
workspaceContext.update(newWorkspace);
setNewWorkspace('');
}

useEffect(() => {
if (data?.workspaces.length > 0) {
setAwailableWorkspaces(data.workspaces);
} else {
setAwailableWorkspaces([]);
}
}, [data]);

return (
<>
<TextField
variant="standard"
sx={{
color: Theme.palette.text.secondary,
backgroundColor: Theme.palette.background.default,
borderRadius: '10px',
height: '40px',
px: '10px',
}}
label={languageContext.language === 'en' ? 'Create new workspace' : 'Pridėti darbo aplinką'}
value={newWorkspace}
onChange={(e) => setNewWorkspace(e.target.value)}
/>
<Button
variant="contained"
sx={{
color: Theme.palette.text.secondary,
backgroundColor: Theme.palette.background.default,
borderRadius: '10px',
height: '40px',
px: '10px',
}}
onClick={() => createNewWorkspace()}
>
{languageContext.language === 'en' ? 'Create' : 'Sukurti'}
</Button>
{awailableWorkspaces.length > 0 && !isFetching && !isLoading && (
<>
<Select
variant="standard"
IconComponent={styled(ArrowDropDownIcon)({
width: '24px',
height: '24px',
})}
disableInjectingGlobalStyles={true}
disableUnderline={true}
sx={{
color: Theme.palette.text.secondary,
backgroundColor: Theme.palette.background.default,
borderRadius: '10px',
height: '40px',
px: '10px',
}}
value={workspaceContext.workspace || ''}
onChange={(e) => workspaceContext.update(e.target.value as string)}
>
{
awailableWorkspaces.map((value) => (
<MenuItem
key={value}
value={value}
sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}
>
{value}
<IconButton
edge="end"
aria-label="delete"
size="small"
onClick={ async (e) => {
e.stopPropagation();
await deleteWorkspace.mutate(value);
workspaceContext.update('');
}}
>
<DeleteForeverIcon sx={{ height: '40px' }} />
</IconButton>
</MenuItem>
))}
</Select>
<FileInstance />
</>
)}
</>
);
}

export default WorkspaceInstance;
57 changes: 57 additions & 0 deletions GUI/front-end/src/components/fileInput/FileInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Button } from "@mui/material";
import { useRef } from "react";
import { useLanguageContext } from "../../contexts";

function FileInput({ onFileSelect } : { onFileSelect: (file: string | ArrayBuffer | null, name: string) => void}){
const languageContext = useLanguageContext();
const inputRef = useRef<HTMLInputElement>(null);

const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
if(e.target.files && e.target.files.length > 0){
const file = e.target.files[0];

if(!['text/plain'].includes(file.type)){
alert('Please select a text file');
return;
}

const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
onFileSelect(reader.result, file.name);
};

if (inputRef.current) {
inputRef.current.value = '';
}
}


};

const onFileInputClick = () => {
if (inputRef.current) {
inputRef.current.click();
}
};

return (
<div>
<input
type="file"
accept=".txt"
ref={inputRef}
onChange={handleFileSelect}
style={{ display: 'none' }}
/>
<Button
variant="contained"
onClick={onFileInputClick}
>
{languageContext.language === 'en' ? 'Select file' : 'Pasirinkti failą'}
</Button>
</div>
);
}

export default FileInput;
1 change: 1 addition & 0 deletions GUI/front-end/src/contexts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export { ApplicationContextProvider } from './application/ApplicationContextProv
export { useApplicationContext } from './application/UseApplicationContext';
export { ToolContextProvider } from './tool/ToolContextProvider';
export { useToolContext } from './tool/UseToolContext';
export { WorkspaceContextProvider, WorkspaceContext } from './tool/WorkspaceContextProvider';
4 changes: 4 additions & 0 deletions GUI/front-end/src/contexts/tool/UseWorkspaceContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { useContext } from "react";
import { WorkspaceContext } from "./WorkspaceContextProvider";

export const useWorkspaceContext = () => useContext(WorkspaceContext);
29 changes: 29 additions & 0 deletions GUI/front-end/src/contexts/tool/WorkspaceContextProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React, { createContext, useState } from "react";

interface WorkspaceContextProps {
workspace: string | null;
update: (newWorkspace: string) => void;
}

export const WorkspaceContext = createContext<WorkspaceContextProps>({
workspace: null,
update: () => {},
})

interface Props {
children?: React.ReactNode;
}

export const WorkspaceContextProvider: React.FC<Props> = ({ children }) => {
const [workspace, setWorkspace] = useState<string | null>(null);

function updateWorkspace(newWorkspace: string) {
setWorkspace(newWorkspace);
}

return (
<WorkspaceContext.Provider value={{ workspace, update: updateWorkspace }}>
{children}
</WorkspaceContext.Provider>
)
}
2 changes: 1 addition & 1 deletion GUI/front-end/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { useGetUsers } from './user/userHook';
export { useSendRequest } from './request/requestHook';
export { useGetWorkspaces, useCreateWorkspace, useDeleteWorkspace, useGetWorkspaceFiles } from './workspace/workspaceHook';
export { useGetWorkspaces, useCreateWorkspace, useDeleteWorkspace, useGetWorkspaceFiles, useUploadFile, useDeleteFile } from './workspace/workspaceHook';
Loading

0 comments on commit 5252473

Please sign in to comment.