diff --git a/src/pages/reportUpload/index.css b/src/pages/reportUpload/index.css
new file mode 100644
index 00000000..431a4529
--- /dev/null
+++ b/src/pages/reportUpload/index.css
@@ -0,0 +1,5 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer components {}
\ No newline at end of file
diff --git a/src/pages/reportUpload/index.tsx b/src/pages/reportUpload/index.tsx
new file mode 100644
index 00000000..d87e8ecb
--- /dev/null
+++ b/src/pages/reportUpload/index.tsx
@@ -0,0 +1,19 @@
+import React from "react";
+
+import { Container, Typography } from "@mui/material";
+import CertificationMetadataForm from "components/CertificationMetadataForm";
+
+import "./index.css";
+
+const ReportUpload = () => {
+ return (
+
+
+ Upload an Audit Report
+
+
+
+ );
+};
+
+export default ReportUpload;
\ No newline at end of file
diff --git a/src/pages/session/components/AppLayout.tsx b/src/pages/session/components/AppLayout.tsx
index 8a0218b1..45ff3290 100644
--- a/src/pages/session/components/AppLayout.tsx
+++ b/src/pages/session/components/AppLayout.tsx
@@ -1,9 +1,6 @@
-import React, { useEffect } from 'react';
+import React from 'react';
import { Outlet } from "react-router-dom";
-import { useAppDispatch } from "store/store";
-import { fetchProfile } from 'store/slices/auth.slice';
-
import { LocalStorageKeys } from "constants/constants";
import { Box, Alert } from "@mui/material";
@@ -31,27 +28,17 @@ const Banner = () => {
) : null;
}
-const AppLayout = () => {
-
- const dispatch = useAppDispatch();
-
- useEffect(() => {
- dispatch(fetchProfile({}))
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [])
-
- return (
-
-
-
-
-
-
-
-
+const AppLayout = () => (
+
+
+
+
+
+
+
- );
-}
+
+);
export default AppLayout;
\ No newline at end of file
diff --git a/src/pages/session/index.tsx b/src/pages/session/index.tsx
index d8e8233c..9ca3e74c 100644
--- a/src/pages/session/index.tsx
+++ b/src/pages/session/index.tsx
@@ -3,7 +3,7 @@ import { Outlet, useLocation, useNavigate } from "react-router-dom";
import { Box, CircularProgress } from "@mui/material";
import { useAppDispatch, useAppSelector } from "store/store";
-import { fetchSession } from "store/slices/auth.slice";
+import { fetchSession, fetchProfile } from "store/slices/auth.slice";
import AppLayout from './components/AppLayout';
@@ -14,10 +14,14 @@ export default () => {
const navigate = useNavigate();
const location = useLocation();
- const { hasAnActiveSubscription, isSessionFetched } = useAppSelector((state) => state.auth);
+ const { hasAnActiveSubscription, isSessionFetched, profile } = useAppSelector((state) => state.auth);
useEffect(() => { dispatch(fetchSession({})); }, []);
+ useEffect(() => {
+ if (hasAnActiveSubscription) dispatch(fetchProfile({}));
+ }, [hasAnActiveSubscription]);
+
useEffect(() => {
if (location.pathname === '/' && hasAnActiveSubscription) {
navigate('/home');
@@ -27,7 +31,8 @@ export default () => {
}
}, [hasAnActiveSubscription, isSessionFetched, location.pathname]);
- if (!isSessionFetched) {
+ // If the session is being fetched or if the user has an active subscription and the profile is being fetched, a loading will be displayed
+ if (!isSessionFetched || (hasAnActiveSubscription && profile === null)) {
return (
diff --git a/src/pages/testingHistory/index.tsx b/src/pages/testingHistory/index.tsx
index 8377de6d..695a21cf 100644
--- a/src/pages/testingHistory/index.tsx
+++ b/src/pages/testingHistory/index.tsx
@@ -6,7 +6,7 @@ import { useNavigate } from "react-router";
import { fetchData } from "api/api";
-import { Container, Box, Button, IconButton } from "@mui/material";
+import { Container, Box, Button, IconButton, Typography } from "@mui/material";
import DeleteIcon from '@mui/icons-material/Delete';
import RefreshIcon from '@mui/icons-material/Refresh';
@@ -380,8 +380,10 @@ const TestHistory = () => {
};
return (
-
- Testing history
+
+
+ Testing history
+
;
diff --git a/src/store/slices/auth.slice.ts b/src/store/slices/auth.slice.ts
index 98625e6f..a5d817c2 100644
--- a/src/store/slices/auth.slice.ts
+++ b/src/store/slices/auth.slice.ts
@@ -7,22 +7,22 @@ import { fetchData } from "api/api";
import type { RootState } from "../rootReducer";
export interface UserProfile {
- address?: string,
- companyName?: string,
- contactEmail?: string,
+ address?: string;
+ email?: string;
+ fullName?: string;
+ companyName?: string;
+ contactEmail?: string;
+ linkedin?: string | null;
+ twitter?: string | null;
+ website?: string | null;
dapp: {
- githubToken?: string | null,
- name: string,
- owner: string,
- repo: string,
- subject?: string,
- version: string
- } | null,
- email?: string,
- fullName?: string,
- linkedin?: string,
- twitter?: string,
- website?: string
+ name: string;
+ owner: string;
+ repo: string;
+ version: string;
+ githubToken?: string | null;
+ subject?: string;
+ } | null;
}
interface AuthState {
diff --git a/src/store/slices/reportUpload.slice.ts b/src/store/slices/reportUpload.slice.ts
new file mode 100644
index 00000000..1d1311c3
--- /dev/null
+++ b/src/store/slices/reportUpload.slice.ts
@@ -0,0 +1,106 @@
+import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
+import { fetchData } from "api/api";
+
+export interface ReportUploadRequest {
+ certificationLevel?: number;
+ summary: string;
+ disclaimer: string;
+ subject?: string;
+ report?: string[];
+ certificateIssuer: {
+ name: string;
+ logo?: string;
+ social: {
+ contact: string;
+ discord?: string;
+ twitter?: string;
+ github?: string;
+ website: string;
+ };
+ };
+ scripts: {
+ scriptHash: string;
+ contractAddress: string;
+ smartContractInfo: {
+ era?: string;
+ compiler?: string;
+ compilerVersion?: string;
+ optimizer?: string;
+ optimizerVersion?: string;
+ progLang?: string;
+ repository?: string;
+ };
+ }[];
+}
+
+interface ReportUploadState {
+ loading: boolean;
+ success: boolean;
+ errorMessage: string|null;
+ onchain: any|null;
+ offchain: any|null;
+ subject: string|null;
+ uuid: string|null;
+}
+
+const initialState: ReportUploadState = {
+ loading: false,
+ success: false,
+ errorMessage: null,
+ onchain: null,
+ offchain: null,
+ subject: null,
+ uuid: null,
+};
+
+export const sendReport = createAsyncThunk("sendReport", async (payload: { request: ReportUploadRequest, uuid?: string }, { rejectWithValue }) => {
+ try {
+ const response = await fetchData.post(payload.uuid ? `/run/${payload.uuid}/certificate` : '/auditor/reports', payload.request);
+ if (response.status !== 200) throw { response };
+ return {
+ onchain: response.data.onchain,
+ offchain: response.data.offchain,
+ subject: payload.request.subject,
+ uuid: payload.uuid || null
+ };
+ } catch (error: any) {
+ let errorMessage = 'Something went wrong. Please try again.';
+ if (error?.response?.data) {
+ errorMessage = `${error.response.statusText} - ${error.response.data}`;
+ }
+ return rejectWithValue(errorMessage);
+ }
+});
+
+export const reportUploadSlice = createSlice({
+ name: "reportUpload",
+ initialState,
+ reducers: {},
+ extraReducers: (builder) => {
+ builder
+ .addCase(sendReport.pending, (state) => {
+ state.loading = true;
+ state.success = false;
+ state.errorMessage = null;
+ state.onchain = null;
+ state.offchain = null;
+ state.subject = null;
+ state.uuid = null;
+ })
+ .addCase(sendReport.fulfilled, (state, actions) => {
+ state.loading = false;
+ state.success = true;
+ state.onchain = actions.payload.onchain;
+ state.offchain = actions.payload.offchain;
+ state.subject = actions.payload.subject || null;
+ state.uuid = actions.payload.uuid;
+ })
+ .addCase(sendReport.rejected, (state, actions) => {
+ state.loading = false;
+ state.success = false;
+ state.errorMessage = actions.payload as string;
+ })
+ },
+});
+
+export default reportUploadSlice.reducer;
\ No newline at end of file
diff --git a/src/utils/utils.ts b/src/utils/utils.ts
index 5b806b8c..0dd060fc 100644
--- a/src/utils/utils.ts
+++ b/src/utils/utils.ts
@@ -56,11 +56,6 @@ export const formatTimeToReadable = (duration: number) => {
return timeStr
}
-// unused now
-// export const convertAdaToLovelace = (fee_ada: number) => {
-// return BigNum.from_str((fee_ada * 1000000).toString())
-// }
-
export const getObjectByPath = (object: { [x: string]: any }, path: string): any => {
path = path.replace(/\[(\w+)\]/g, ".$1"); // convert indexes to properties
path = path.replace(/^\./, ""); // strip a leading dot
@@ -86,6 +81,15 @@ export const transformEmptyStringToNullInObj = (obj: any) => {
return obj;
}
+// Deletes all the keys with empty strings from an object
+export const removeEmptyStringsDeep = (obj: any) => {
+ Object.keys(obj).forEach(key =>
+ (obj[key] && typeof obj[key] === 'object' && removeEmptyStringsDeep(obj[key])) ||
+ (obj[key] === '' && delete obj[key])
+ );
+ return obj;
+};
+
export const getErrorMessage = (errorObj: any) => {
let errorMsg = "Something wrong occurred. Please try again later.";
if (typeof errorObj === "string") {