diff --git a/.github/workflows/.gitkeep b/.github/workflows/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/components/AttributeAdder/AttributeAdder.tsx b/src/components/AttributeAdder/AttributeAdder.tsx index 0e2d6fc..cd5a0a9 100644 --- a/src/components/AttributeAdder/AttributeAdder.tsx +++ b/src/components/AttributeAdder/AttributeAdder.tsx @@ -1,10 +1,13 @@ import { useEffect, useState } from 'react'; import './AttributeAdder.scss'; import Icon from '../Icon/Icon'; +import { useIsDocbaseTaskRunning } from '../../providers/DocBaseTaskProvider'; interface Props { - populateAble: boolean; onListChange: (list: string[]) => void; + populateAble?: boolean; + rerunAble?: boolean; + onRerun?: (list: string[]) => void; initialList?: string[]; } @@ -12,12 +15,17 @@ interface Props { * A component that allows the user to add attributes to there document base */ function AttributeAdder({ - populateAble = true, onListChange, + populateAble = false, + rerunAble = false, initialList = [], + // eslint-disable-next-line @typescript-eslint/no-unused-vars + onRerun = (_list: string[]) => {}, }: Props) { const [attList, setAttList] = useState([]); + const isDocBaseTaskRunning = useIsDocbaseTaskRunning(); + useEffect( () => { if ( @@ -57,11 +65,28 @@ function AttributeAdder({ } }; + const isListEqualToInitialList = () => { + if (attList.length !== initialList.length) { + return false; + } + attList.forEach((att) => { + if (!initialList.includes(att)) { + return false; + } + }); + return true; + }; + return (
+ {rerunAble && ( +

+ Modify the attributes to rerun the document base. +

+ )} {attList.length === 0 && (

- Enter a attribute... + Enter an attribute...

)} {attList.map((att, i) => { @@ -121,6 +146,17 @@ function AttributeAdder({ Attributes )} + {rerunAble && + attList.length > 0 && + !isListEqualToInitialList() && + !isDocBaseTaskRunning() && ( + + )}
); } diff --git a/src/components/DocbaseViewer/DocbaseViewer.tsx b/src/components/DocbaseViewer/DocbaseViewer.tsx index 59eec0f..3dd8aac 100644 --- a/src/components/DocbaseViewer/DocbaseViewer.tsx +++ b/src/components/DocbaseViewer/DocbaseViewer.tsx @@ -1,3 +1,4 @@ +import { useUpdateDocbaseAttributesTask } from '../../providers/DocBaseTaskProvider'; import DocBase from '../../types/DocBase'; import AttributeAdder from '../AttributeAdder/AttributeAdder'; import NuggetDocumentViewer from '../NuggetViewer/NuggetDocumentViewer'; @@ -13,6 +14,8 @@ interface Props { * @param docBase The docbase to view */ function DocbaseViewer({ docBase, onClose }: Props) { + const updateDocbaseAttributesTask = useUpdateDocbaseAttributesTask(); + return ( <>
-

{docBase.name}

+

+ DocBase: {docBase.name} +

Attributes

{ docBase.attributes = list; }} initialList={docBase.attributes} + onRerun={(list: string[]) => { + updateDocbaseAttributesTask( + docBase.organizationId, + docBase.name, + list + ); + onClose(); + }} > {/*
diff --git a/src/components/Navbar/Navbar.tsx b/src/components/Navbar/Navbar.tsx index a33bc69..14b122b 100644 --- a/src/components/Navbar/Navbar.tsx +++ b/src/components/Navbar/Navbar.tsx @@ -1,7 +1,7 @@ import { Link, useNavigate } from 'react-router-dom'; import './Navbar.scss'; import { useState } from 'react'; -import { useGetUsername } from '../../providers/UserProvider'; +import { useGetUsername, useLoggedIn } from '../../providers/UserProvider'; import { useIsDarkTheme, useToggleTheme } from '../../providers/ThemeProvider'; import Icon from '../Icon/Icon'; @@ -12,6 +12,7 @@ function Navbar() { const navigate = useNavigate(); const getUserName = useGetUsername(); + const isLoggedIn = useLoggedIn(); const [username] = useState(getUserName()); @@ -70,7 +71,7 @@ function Navbar() { {/* */} Help - {username !== '' ? ( + {isLoggedIn() ? (
{username.slice(0, 1).toUpperCase()} diff --git a/src/providers/DocBaseTaskProvider.tsx b/src/providers/DocBaseTaskProvider.tsx index a70c360..ef1b5ec 100644 --- a/src/providers/DocBaseTaskProvider.tsx +++ b/src/providers/DocBaseTaskProvider.tsx @@ -21,6 +21,11 @@ const DocBaseTaskContext = React.createContext({ _documentIDs: number[], _attributes: string[] ) => {}, + updateDocbaseAttributesTask: ( + _organizationId: number, + _baseName: string, + _attributes: string[] + ) => {}, loadDocbaseTask: (_organizationId: number, _baseName: string) => {}, }); @@ -46,6 +51,17 @@ export function useCreateDocbaseTask() { return context.createDocbaseTask; } +// eslint-disable-next-line react-refresh/only-export-components +export function useUpdateDocbaseAttributesTask() { + const context = React.useContext(DocBaseTaskContext); + if (!context) { + throw new Error( + 'useUpdateDocbaseAttributesTask must be used within a DocBaseTaskProvider' + ); + } + return context.updateDocbaseAttributesTask; +} + // eslint-disable-next-line react-refresh/only-export-components export function useLoadDocbaseTask() { const context = React.useContext(DocBaseTaskContext); @@ -146,7 +162,11 @@ export function DocBaseTaskProvider({ children }: Props) { setLoadingScreen(false); playAudio(MyAudio.SUCCESS); - const docBase = new DocBase(baseName, attributes); + const docBase = new DocBase( + baseName, + organizationId, + attributes + ); for (const nugget of res.meta.document_base_to_ui.msg .nuggets) { try { @@ -190,6 +210,121 @@ export function DocBaseTaskProvider({ children }: Props) { }, intervalTime); }; + const updateDocbaseAttributesTask = async ( + organizationId: number, + baseName: string, + attributes: string[] + ) => { + if (isRunning) { + Logger.warn( + 'Docbase task is already running, cannot start another' + ); + return; + } + + // start the task + const taskId = await APIService.updateDocumentBaseAttributes( + organizationId, + baseName, + attributes + ); + + if (taskId == undefined) { + showNotification('Error', 'Failed to update Docbase ' + baseName); + return; + } + + Logger.log('Task: Update Attributes Docbase ' + baseName); + Logger.log('Task ID: ' + taskId); + + sessionStorage.setItem('docbaseId', taskId); + setLoadingScreen( + true, + 'Updating Docbase ' + baseName + '...', + 'Please wait...', + taskId + ); + + setLoadingScreenLock(true); + setIsRunning(true); + + const updateInterval = setInterval(() => { + // TODO use type + // eslint-disable-next-line @typescript-eslint/no-explicit-any + APIService.getTaskStatus(taskId).then((res): any => { + Logger.log(res); + if ( + res == undefined || + res.state.toUpperCase().trim() === 'FAILURE' + ) { + setLoadingScreenLock(false); + setLoadingScreen(false); + playAudio(MyAudio.ERROR); + + showNotification( + 'Error', + 'Failed to update Docbase ' + baseName + ); + sessionStorage.removeItem('docbaseId'); + setDocBase(undefined); + setIsRunning(false); + clearInterval(updateInterval); + return; + } + + if (res.state === 'SUCCESS') { + setLoadingScreenLock(false); + setLoadingScreen(false); + playAudio(MyAudio.SUCCESS); + + const docBase = new DocBase( + baseName, + organizationId, + attributes + ); + for (const nugget of res.meta.document_base_to_ui.msg + .nuggets) { + try { + docBase.addNugget( + nugget.document.name, + nugget.document.text, + nugget.start_char, + nugget.end_char + ); + } catch (error) { + showNotification( + 'Error', + 'Something went wrong translating the nuggets.' + ); + } + } + sessionStorage.removeItem('docbaseId'); + setDocBase(docBase); + setIsRunning(false); + clearInterval(updateInterval); + return; + } + + let info = res.state; + + if (res.meta.status !== undefined) { + info = res.meta.status; + } + + info += info.endsWith('...') ? '' : '...'; + + // update loading screen + setLoadingScreen( + true, + 'Updating Docbase ' + baseName + '...', + info, + taskId, + true + ); + }); + }, intervalTime); + }; + const loadDocbaseTask = async ( organizationId: number, baseName: string @@ -255,6 +390,7 @@ export function DocBaseTaskProvider({ children }: Props) { const docBase = new DocBase( baseName, + organizationId, res.meta.document_base_to_ui.msg.attributes ?? [] ); for (const nugget of res.meta.document_base_to_ui.msg @@ -316,6 +452,7 @@ export function DocBaseTaskProvider({ children }: Props) { isDocbaseTaskRunning: isDocbaseTaskRunning, createDocbaseTask: createDocbaseTask, loadDocbaseTask: loadDocbaseTask, + updateDocbaseAttributesTask: updateDocbaseAttributesTask, }} > {docBase && } diff --git a/src/types/DocBase.ts b/src/types/DocBase.ts index 6eb06ef..a77be5d 100644 --- a/src/types/DocBase.ts +++ b/src/types/DocBase.ts @@ -5,11 +5,13 @@ import NuggetDocument from './NuggetDocument'; */ class DocBase { readonly name: string; + readonly organizationId: number; attributes: string[]; docs: NuggetDocument[]; - constructor(name: string, attributes: string[]) { + constructor(name: string, organizationId: number, attributes: string[]) { this.name = name; + this.organizationId = organizationId; this.attributes = attributes; this.docs = []; } diff --git a/src/utils/ApiService.ts b/src/utils/ApiService.ts index be43b8b..be253a1 100644 --- a/src/utils/ApiService.ts +++ b/src/utils/ApiService.ts @@ -17,10 +17,12 @@ class APIService { static async login(username: string, password: string): Promise { try { const url = `${this.host}/login`; - const resp = await axios.post(url, { - username: username, - password: password, - }); + const resp = await axios + .post(url, { + username: username, + password: password, + }) + .catch(this.handleCatch); if (resp.status === 200) { sessionStorage.setItem('user-token', resp.data.token); return true; @@ -80,25 +82,30 @@ class APIService { ): Promise { try { const url = `${this.host}/deleteUser`; - const resp = await axios.post( - url, - { - username: username, - password: password, - }, - { - headers: { - Authorization: this.getUserToken(), + const resp = await axios + .post( + url, + { + username: username, + password: password, }, - } - ); + { + headers: { + Authorization: this.getUserToken(), + }, + } + ) + .catch(this.handleCatch); + if (resp.status === 204) { this.clearUserToken(); return true; } return false; } catch (err) { - Logger.error(err); + //Logger.error(err); + console.log('hallo000'); + return false; } } @@ -110,11 +117,16 @@ class APIService { static async getOrganizations(): Promise { try { const url = `${this.host}/getOrganisations`; - const resp = await axios.get(url, { - headers: { - Authorization: this.getUserToken(), - }, - }); + const resp = await axios + .get(url, { + headers: { + Authorization: this.getUserToken(), + }, + }) + .catch(this.handleCatch); + if (resp.status == 401) { + Logger.error('Unauthorized'); + } if (resp.status == 200) { return resp.data.organisation_ids as number[]; } @@ -132,13 +144,16 @@ class APIService { static async getOrganizationNames(): Promise { try { const url = `${this.host}/getOrganisationNames`; - const resp = await axios.get<{ - organisations: Organization[]; - }>(url, { - headers: { - Authorization: this.getUserToken(), - }, - }); + const resp = await axios + .get<{ + organisations: Organization[]; + }>(url, { + headers: { + Authorization: this.getUserToken(), + }, + }) + .catch(this.handleCatch); + if (resp.status == 200) { return resp.data.organisations; } @@ -158,11 +173,13 @@ class APIService { // NILS MACH MA TEST try { const url = `${this.host}/get/user/suggestion/${prefix}`; - const resp = await axios.get(url, { - headers: { - Authorization: this.getUserToken(), - }, - }); + const resp = await axios + .get(url, { + headers: { + Authorization: this.getUserToken(), + }, + }) + .catch(this.handleCatch); if (resp.status == 200) { return resp.data.usernames as string[]; } @@ -183,11 +200,13 @@ class APIService { ): Promise { try { const url = `${this.host}/getOrganisationName/${id}`; - const resp = await axios.get(url, { - headers: { - Authorization: this.getUserToken(), - }, - }); + const resp = await axios + .get(url, { + headers: { + Authorization: this.getUserToken(), + }, + }) + .catch(this.handleCatch); if (resp.status == 200) { return resp.data.organisation_name as string; } @@ -208,17 +227,19 @@ class APIService { ): Promise { try { const url = `${this.host}/createOrganisation`; - const resp = await axios.post( - url, - { - organisationName: orgName, - }, - { - headers: { - Authorization: this.getUserToken(), + const resp = await axios + .post( + url, + { + organisationName: orgName, }, - } - ); + { + headers: { + Authorization: this.getUserToken(), + }, + } + ) + .catch(this.handleCatch); if (resp.status === 200) { sessionStorage.setItem( 'organisation', @@ -243,17 +264,19 @@ class APIService { static async leaveOrganization(orgId: number): Promise { try { const url = `${this.host}/leaveOrganisation`; - const resp = await axios.post( - url, - { - organisationId: orgId, - }, - { - headers: { - Authorization: this.getUserToken(), + const resp = await axios + .post( + url, + { + organisationId: orgId, }, - } - ); + { + headers: { + Authorization: this.getUserToken(), + }, + } + ) + .catch(this.handleCatch); if (resp.status === 200) { return resp.data.status; } @@ -275,11 +298,13 @@ class APIService { ): Promise { try { const url = `${this.host}/getOrganisationMembers/${orgId}`; - const resp = await axios.get(url, { - headers: { - Authorization: this.getUserToken(), - }, - }); + const resp = await axios + .get(url, { + headers: { + Authorization: this.getUserToken(), + }, + }) + .catch(this.handleCatch); if (resp.status === 200) { return resp.data.members as string[]; } @@ -314,11 +339,7 @@ class APIService { }, } ) - .catch((err) => { - Logger.error(err); - - return err.response; - }); + .catch(this.handleCatch); if (resp.status === 200) { return ''; } @@ -340,16 +361,14 @@ class APIService { } body.append('organisationId', organisationId.toString()); - const resp = await axios.post( - `${this.host}/data/upload/file`, - body, - { + const resp = await axios + .post(`${this.host}/data/upload/file`, body, { headers: { 'Content-Type': 'multipart/form-data', Authorization: this.getUserToken(), }, - } - ); + }) + .catch(this.handleCatch); if (resp.status === 201) { return 'File uploaded successfully'; } @@ -373,14 +392,16 @@ class APIService { ): Promise { // NILS MACH MA TEST try { - const response = await axios.get( - `${this.host}/data/organization/get/files/${organizationID}`, - { - headers: { - Authorization: this.getUserToken(), - }, - } - ); + const response = await axios + .get( + `${this.host}/data/organization/get/files/${organizationID}`, + { + headers: { + Authorization: this.getUserToken(), + }, + } + ) + .catch(this.handleCatch); if (response.status === 200) { return response.data as MyDocument[]; } @@ -401,14 +422,16 @@ class APIService { ): Promise { // NILS MACH MA TEST try { - const response = await axios.get( - `${this.host}/data/organization/get/documentbase/${organizationID}`, - { - headers: { - Authorization: this.getUserToken(), - }, - } - ); + const response = await axios + .get( + `${this.host}/data/organization/get/documentbase/${organizationID}`, + { + headers: { + Authorization: this.getUserToken(), + }, + } + ) + .catch(this.handleCatch); if (response.status === 200) { return response.data as MyDocument[]; } @@ -431,18 +454,20 @@ class APIService { ): Promise { // NILS MACH MA TEST try { - const response = await axios.post( - `${this.host}/data/update/file/content`, - { - documentId: documentId, - newContent: newContent, - }, - { - headers: { - Authorization: this.getUserToken(), + const response = await axios + .post( + `${this.host}/data/update/file/content`, + { + documentId: documentId, + newContent: newContent, }, - } - ); + { + headers: { + Authorization: this.getUserToken(), + }, + } + ) + .catch(this.handleCatch); if (response.status === 200) { return response.data.status; } @@ -461,17 +486,19 @@ class APIService { static async deleteDocument(documentId: number): Promise { // NILS MACH MA TEST try { - const response = await axios.post( - `${this.host}/data/file/delete`, - { - documentId: documentId, - }, - { - headers: { - Authorization: this.getUserToken(), + const response = await axios + .post( + `${this.host}/data/file/delete`, + { + documentId: documentId, }, - } - ); + { + headers: { + Authorization: this.getUserToken(), + }, + } + ) + .catch(this.handleCatch); if (response.status === 200) { return response.data.status; } @@ -508,21 +535,38 @@ class APIService { body.append('document_ids', documentIDs.join(',')); body.append('attributes', attributes.join(',')); body.append('authorization', this.getUserToken()); - const resp = await axios.post(url, body); - /* resp = await axios.post( - url, - { - organisationId: organizationId, - baseName: baseName, - document_ids: documentIDs, - attributes: attributes, - }, - { - headers: { - Authorization: this.getUserToken(), - }, - } - ); */ + const resp = await axios.post(url, body).catch(this.handleCatch); + return resp.data.task_id; + } catch (err) { + Logger.error(err); + return undefined; + } + } + + /** + * Update the attributes of a documentbase. + * @param organizationId The ID of the organization + * @param baseName The name for the docbase + * @param attributes An array of the new attributes that should be used for the docbase + * @returns The ID of the created task or undefined if the creation failed + */ + static async updateDocumentBaseAttributes( + organizationId: number, + baseName: string, + newAttributes: string[] + ): Promise { + // kannste erstmal ignorieren + // NILS MACH MA TEST + + try { + const url = `${this.host}/core/document_base/attributes/update`; + + const body = new FormData(); + body.append('authorization', this.getUserToken()); + body.append('organisationId', organizationId.toString()); + body.append('baseName', baseName); + body.append('attributes', newAttributes.join(',')); + const resp = await axios.post(url, body).catch(this.handleCatch); return resp.data.task_id; } catch (err) { Logger.error(err); @@ -552,7 +596,7 @@ class APIService { body.append('organisationId', organizationId.toString()); body.append('baseName', baseName); body.append('authorization', this.getUserToken()); - const resp = await axios.post(url, body); + const resp = await axios.post(url, body).catch(this.handleCatch); return resp.data.task_id; } catch (err) { Logger.error(err); @@ -575,7 +619,7 @@ class APIService { const url = `${ this.host }/core/status/${this.getUserToken()}/${taskId}`; - const resp = await axios.get(url); + const resp = await axios.get(url).catch(this.handleCatch); return resp.data; } catch (error) { Logger.error(error); @@ -615,6 +659,21 @@ class APIService { */ static clearUserToken() { sessionStorage.removeItem('user-token'); + sessionStorage.removeItem('wannadbuser'); + } + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + static handleCatch(err: any) { + Logger.error(err); + + if (err.response.status === 401) { + Logger.error('Unauthorized!!!'); + // navigate to home page + APIService.clearUserToken(); + window.location.href = window.location.origin; + } + + return err.response; } } diff --git a/src/views/Home/Home.tsx b/src/views/Home/Home.tsx index 82616fd..b9ef08f 100644 --- a/src/views/Home/Home.tsx +++ b/src/views/Home/Home.tsx @@ -1,7 +1,11 @@ import { Link, useNavigate } from 'react-router-dom'; import './Home.scss'; import { useState } from 'react'; -import { useGetUsername, useLogOut } from '../../providers/UserProvider'; +import { + useGetUsername, + useLogOut, + useLoggedIn, +} from '../../providers/UserProvider'; import Navbar from '../../components/Navbar/Navbar'; import FileUpload from '../../components/FileUpload/FileUpload'; import DocBaseOverview from '../../components/DocBaseOverview/DocBaseOverview'; @@ -15,8 +19,15 @@ function Home() { const logOut = useLogOut(); const [username] = useState(getUserName()); + const isLoggedIn = useLoggedIn(); - if (username !== '') { + /* useEffect(() => { + if (!isLoggedIn) { + navigate('/login'); + } + }, []); */ + + if (isLoggedIn()) { return (