{Object.keys(errors).length > 0 && (
setErrors({})} />
diff --git a/src/components/FeedTree/ParentComponent.tsx b/src/components/FeedTree/ParentComponent.tsx
index d3b8649ee..a4a7b5d52 100644
--- a/src/components/FeedTree/ParentComponent.tsx
+++ b/src/components/FeedTree/ParentComponent.tsx
@@ -1,10 +1,12 @@
import React from "react";
+import { Alert } from "antd";
import { useDispatch } from "react-redux";
-import { setFeedTreeProp } from "../../store/feed/actions";
import { PluginInstance } from "@fnndsc/chrisapi";
+import { setFeedTreeProp } from "../../store/feed/actions";
import FeedTree from "./FeedTree";
import { getFeedTree, TreeNodeDatum, getTsNodes } from "./data";
import { useTypedSelector } from "../../store/hooks";
+import { SpinContainer } from "../Common";
import "./FeedTree.css";
interface ParentComponentProps {
@@ -60,11 +62,9 @@ const ParentComponent = (props: ParentComponentProps) => {
changeOrientation={changeOrientation}
/>
) : loading ? (
- Loading...
+
) : error ? (
-
+
) : null;
};
diff --git a/src/components/Feeds/FeedListView.tsx b/src/components/Feeds/FeedListView.tsx
index f2bc8f351..0c7f90b62 100644
--- a/src/components/Feeds/FeedListView.tsx
+++ b/src/components/Feeds/FeedListView.tsx
@@ -1,5 +1,5 @@
-import React, { useContext, useMemo } from "react";
-import { useLocation, useNavigate } from "react-router";
+import React, { useContext } from "react";
+import { useNavigate } from "react-router";
import { useDispatch } from "react-redux";
import { format } from "date-fns";
import { useQuery } from "@tanstack/react-query";
@@ -25,7 +25,7 @@ import SearchIcon from "@patternfly/react-icons/dist/esm/icons/search-icon";
import { Typography } from "antd";
import { cujs } from "chris-utility";
import { useTypedSelector } from "../../store/hooks";
-import { usePaginate } from "./usePaginate";
+import { usePaginate, useSearchQueryParams } from "./usePaginate";
import {
setBulkSelect,
removeBulkSelect,
@@ -46,12 +46,6 @@ import { fetchPublicFeeds, fetchFeeds } from "./utilties";
const { Paragraph } = Typography;
-function useSearchQueryParams() {
- const { search } = useLocation();
-
- return useMemo(() => new URLSearchParams(search), [search]);
-}
-
function useSearchQuery(query: URLSearchParams) {
const page = query.get("page") || 1;
const search = query.get("search") || "";
@@ -79,7 +73,7 @@ const TableSelectable: React.FunctionComponent = () => {
const { data, isLoading, isFetching } = useQuery({
queryKey: ["feeds", searchFolderData],
queryFn: () => fetchFeeds(searchFolderData),
- enabled: isLoggedIn && type === "private",
+ enabled: type === "private",
});
const {
@@ -139,16 +133,15 @@ const TableSelectable: React.FunctionComponent = () => {
activeItem: "analyses",
}),
);
- if (bulkData && bulkData.current) {
+ if (bulkData?.current) {
dispatch(removeAllSelect(bulkData.current));
}
}, [dispatch]);
React.useEffect(() => {
- if (!type) {
- const feedType = isLoggedIn ? "private" : "public";
+ if (!type || (!isLoggedIn && type === "private")) {
navigate(
- `/feeds?search=${search}&searchType=${searchType}&page=${page}&perPage=${perPage}&type=${feedType}`,
+ `/feeds?search=${search}&searchType=${searchType}&page=${page}&perPage=${perPage}&type=public`,
);
}
}, [isLoggedIn, navigate, perPage, page, searchType, search, type]);
@@ -187,7 +180,7 @@ const TableSelectable: React.FunctionComponent = () => {
title={`New and Existing Analyses (${
type === "private" && data && data.totalFeedsCount
? data.totalFeedsCount
- : publicFeeds && publicFeeds.totalFeedsCount
+ : publicFeeds?.totalFeedsCount
? publicFeeds.totalFeedsCount
: 0
})`}
@@ -286,6 +279,7 @@ const TableSelectable: React.FunctionComponent = () => {
bulkSelect={bulkSelect}
columnNames={columnNames}
allFeeds={feedsToDisplay}
+ type={type}
/>
);
})}
@@ -316,9 +310,16 @@ interface TableRowProps {
size: string;
status: string;
};
+ type: string;
}
-function TableRow({ feed, allFeeds, bulkSelect, columnNames }: TableRowProps) {
+function TableRow({
+ feed,
+ allFeeds,
+ bulkSelect,
+ columnNames,
+ type,
+}: TableRowProps) {
const navigate = useNavigate();
const [intervalMs, setIntervalMs] = React.useState(2000);
const { isDarkTheme } = useContext(ThemeContext);
@@ -352,20 +353,19 @@ function TableRow({ feed, allFeeds, bulkSelect, columnNames }: TableRowProps) {
const { id, name: feedName, creation_date, creator_username } = feed.data;
const { dispatch } = usePaginate();
- const progress = feedResources[id] && feedResources[id].details.progress;
+ const progress = feedResources[id]?.details.progress;
- const size = feedResources[id] && feedResources[id].details.size;
- const feedError = feedResources[id] && feedResources[id].details.error;
- const runtime = feedResources[id] && feedResources[id].details.time;
+ const size = feedResources[id]?.details.size;
+ const feedError = feedResources[id]?.details.error;
+ const runtime = feedResources[id]?.details.time;
- const feedProgressText =
- feedResources[id] && feedResources[id].details.feedProgressText;
+ const feedProgressText = feedResources[id]?.details.feedProgressText;
let threshold = Infinity;
// If error in a feed => reflect in progres
- let title = (progress ? progress : 0) + "%";
+ let title = `${progress ? progress : 0}%`;
let color = "blue";
if (feedError) {
@@ -374,7 +374,7 @@ function TableRow({ feed, allFeeds, bulkSelect, columnNames }: TableRowProps) {
}
// If initial node in a feed fails
- if (progress == 0 && feedError) {
+ if (progress === 0 && feedError) {
color = "#00ff00";
title = "❌";
}
@@ -384,7 +384,7 @@ function TableRow({ feed, allFeeds, bulkSelect, columnNames }: TableRowProps) {
color = "#00ff00";
threshold = progress;
}
- if (progress == 100) {
+ if (progress === 100) {
title = "✔️";
}
@@ -413,7 +413,7 @@ function TableRow({ feed, allFeeds, bulkSelect, columnNames }: TableRowProps) {
- {feed && new Date(feed.data.creation_date).toDateString()}
+ {data && new Date(data?.feed?.data.creation_date).toDateString()}
diff --git a/src/components/Login/index.tsx b/src/components/Login/index.tsx
index 6a8d32730..1c8a50d56 100644
--- a/src/components/Login/index.tsx
+++ b/src/components/Login/index.tsx
@@ -19,6 +19,7 @@ import {
import ExclamationCircleIcon from "@patternfly/react-icons/dist/esm/icons/exclamation-circle-icon";
import { setAuthTokenSuccess } from "../../store/user/actions";
import "./Login.css";
+import ChrisAPIClient from "../../api/chrisapiclient";
export const SimpleLoginPage: React.FunctionComponent = () => {
const dispatch = useDispatch();
@@ -33,8 +34,8 @@ export const SimpleLoginPage: React.FunctionComponent = () => {
const navigate = useNavigate();
enum LoginErrorMessage {
- invalidCredentials = `Invalid Credentials`,
- serverError = `There was a problem connecting to the server!`,
+ invalidCredentials = "Invalid Credentials",
+ serverError = "There was a problem connecting to the server!",
}
async function handleSubmit(
@@ -45,10 +46,34 @@ export const SimpleLoginPage: React.FunctionComponent = () => {
event.preventDefault();
const authURL = import.meta.env.VITE_CHRIS_UI_AUTH_URL;
- let token;
+ let token: string;
try {
token = await ChrisApiClient.getAuthToken(authURL, username, password);
+ if (token && username) {
+ const oneDayToSeconds = 24 * 60 * 60;
+ setCookie(`${username}_token`, token, {
+ path: "/",
+ maxAge: oneDayToSeconds,
+ });
+ setCookie("username", username, {
+ path: "/",
+ maxAge: oneDayToSeconds,
+ });
+
+ const client = ChrisAPIClient.getClient();
+ const user = await client.getUser();
+
+ dispatch(
+ setAuthTokenSuccess({
+ token,
+ username: username,
+ isStaff: user.data.is_staff,
+ }),
+ );
+
+ navigate("/");
+ }
} catch (error: unknown) {
setShowHelperText(true);
// Allows error message to be displayed in red
@@ -62,25 +87,6 @@ export const SimpleLoginPage: React.FunctionComponent = () => {
: LoginErrorMessage.serverError,
);
}
-
- if (token && username) {
- dispatch(
- setAuthTokenSuccess({
- token,
- username: username,
- }),
- );
- const oneDayToSeconds = 24 * 60 * 60;
- setCookie(`${username}_token`, token, {
- path: "/",
- maxAge: oneDayToSeconds,
- });
- setCookie("username", username, {
- path: "/",
- maxAge: oneDayToSeconds,
- });
- navigate("/");
- }
}
const handleUsernameChange = (
diff --git a/src/components/Signup/SignUpForm.tsx b/src/components/Signup/SignUpForm.tsx
index 38687dc5f..c07fa5b22 100644
--- a/src/components/Signup/SignUpForm.tsx
+++ b/src/components/Signup/SignUpForm.tsx
@@ -14,7 +14,7 @@ import {
HelperText,
HelperTextItem,
} from "@patternfly/react-core";
-import ChrisApiClient from "@fnndsc/chrisapi";
+import ChrisApiClient, { User } from "@fnndsc/chrisapi";
import { Link } from "react-router-dom";
import { has } from "lodash";
import { validate } from "email-validator";
@@ -27,7 +27,11 @@ type Validated = {
};
interface SignUpFormProps {
- setAuthTokenSuccess: (auth: { token: string; username: string }) => void;
+ setAuthTokenSuccess: (auth: {
+ token: string;
+ username: string;
+ isStaff: boolean;
+ }) => void;
isShowPasswordEnabled?: boolean;
showPasswordAriaLabel?: string;
hidePasswordAriaLabel?: string;
@@ -104,8 +108,8 @@ const SignUpForm: React.FC = ({
setLoading(true);
const userURL = import.meta.env.VITE_CHRIS_UI_USERS_URL;
const authURL = import.meta.env.VITE_CHRIS_UI_AUTH_URL;
- let user;
- let token;
+ let user: User;
+ let token: string;
if (userURL) {
try {
@@ -121,6 +125,26 @@ const SignUpForm: React.FC = ({
userState.username,
passwordState.password,
);
+
+ if (user && token) {
+ const oneDayToSeconds = 24 * 60 * 60;
+ setCookie("username", user.data.username, {
+ path: "/",
+ maxAge: oneDayToSeconds,
+ });
+ setCookie(`${user.data.username}_token`, token, {
+ path: "/",
+ maxAge: oneDayToSeconds,
+ });
+ setAuthTokenSuccess({
+ token,
+ username: user.data.username,
+ isStaff: user.data.is_staff,
+ });
+ const then = new URLSearchParams(location.search).get("then");
+ if (then) navigate(then);
+ else navigate("/");
+ }
} catch (error) {
if (has(error, "response")) {
if (has(error, "response.data.username")) {
@@ -157,25 +181,6 @@ const SignUpForm: React.FC = ({
}
}
}
-
- if (user && token) {
- const oneDayToSeconds = 24 * 60 * 60;
- setCookie("username", user.data.username, {
- path: "/",
- maxAge: oneDayToSeconds,
- });
- setCookie(`${user.data.username}_token`, token, {
- path: "/",
- maxAge: oneDayToSeconds,
- });
- setAuthTokenSuccess({
- token,
- username: user.data.username,
- });
- const then = new URLSearchParams(location.search).get("then");
- if (then) navigate(then);
- else navigate("/");
- }
};
const passwordInput = (
@@ -225,7 +230,7 @@ const SignUpForm: React.FC = ({
username: value,
})
}
- >
+ />
{userState.invalidText}
@@ -302,8 +307,11 @@ const SignUpForm: React.FC = ({
};
const mapDispatchToProps = (dispatch: Dispatch) => ({
- setAuthTokenSuccess: (auth: { token: string; username: string }) =>
- dispatch(setAuthTokenSuccess(auth)),
+ setAuthTokenSuccess: (auth: {
+ token: string;
+ username: string;
+ isStaff: boolean;
+ }) => dispatch(setAuthTokenSuccess(auth)),
});
export default connect(null, mapDispatchToProps)(SignUpForm);
diff --git a/src/components/Wrapper/Sidebar.tsx b/src/components/Wrapper/Sidebar.tsx
index dcdf8127f..687fc5fb0 100644
--- a/src/components/Wrapper/Sidebar.tsx
+++ b/src/components/Wrapper/Sidebar.tsx
@@ -137,7 +137,7 @@ const AnonSidebarImpl: React.FC = ({
itemId="analyses"
isActive={sidebarActiveItem === "analyses"}
>
- New and Existing Analyses
+ New and Existing Analyses
Plugins
diff --git a/src/store/feed/reducer.ts b/src/store/feed/reducer.ts
index 4e8e8c14d..0e847d9c4 100644
--- a/src/store/feed/reducer.ts
+++ b/src/store/feed/reducer.ts
@@ -67,15 +67,14 @@ const reducer: Reducer = (
orientation: "vertical",
},
};
- else {
- return {
- ...state,
- feedTreeProp: {
- ...state.feedTreeProp,
- orientation: "horizontal",
- },
- };
- }
+
+ return {
+ ...state,
+ feedTreeProp: {
+ ...state.feedTreeProp,
+ orientation: "horizontal",
+ },
+ };
}
case FeedActionTypes.SET_ALL_SELECT: {
diff --git a/src/store/user/actions.ts b/src/store/user/actions.ts
index 1f94de2a2..b1b0bc3ca 100644
--- a/src/store/user/actions.ts
+++ b/src/store/user/actions.ts
@@ -4,6 +4,7 @@ import { UserActionTypes } from "./types";
export const setAuthTokenSuccess = (auth: {
token: string;
username: string;
+ isStaff: boolean;
}) => action(UserActionTypes.SET_TOKEN_SUCCESS, auth); // NOTE: To be done: Save user token to cookie or session
export const setUserLogout = (username: string) =>
action(UserActionTypes.LOGOUT_USER, username);
diff --git a/src/store/user/reducer.ts b/src/store/user/reducer.ts
index bc0fea80c..c84d2231a 100644
--- a/src/store/user/reducer.ts
+++ b/src/store/user/reducer.ts
@@ -12,6 +12,7 @@ const initialState: IUserState = {
token: token,
isRememberMe: false,
isLoggedIn: token ? true : false,
+ isStaff: false,
};
// ***** NOTE: Working *****
@@ -26,6 +27,7 @@ const reducer: Reducer = (
username: action.payload.username,
token: action.payload.token,
isLoggedIn: true,
+ isStaff: action.payload.isStaff,
};
}
case UserActionTypes.SET_TOKEN_ERROR: {
diff --git a/src/store/user/types.ts b/src/store/user/types.ts
index f97e984c6..42d100693 100644
--- a/src/store/user/types.ts
+++ b/src/store/user/types.ts
@@ -14,6 +14,7 @@ export interface IUserState {
token?: string | null;
isRememberMe?: boolean;
isLoggedIn?: boolean;
+ isStaff: boolean;
}
export const UserActionTypes = keyMirror({