|
@@ -15,6 +15,7 @@ import _ from 'lodash';
|
|
|
import { useMemo } from 'react';
|
|
import { useMemo } from 'react';
|
|
|
import api from "shared/api";
|
|
import api from "shared/api";
|
|
|
import Button from "components/porter/Button";
|
|
import Button from "components/porter/Button";
|
|
|
|
|
+import Container from "components/porter/Container";
|
|
|
|
|
|
|
|
type Props = RouteComponentProps & {
|
|
type Props = RouteComponentProps & {
|
|
|
closeModal: () => void;
|
|
closeModal: () => void;
|
|
@@ -34,19 +35,24 @@ const ProjectSelectionModal: React.FC<Props> = ({
|
|
|
const [clusters, setClusters] = useState<DetailedClusterType[]>([]);
|
|
const [clusters, setClusters] = useState<DetailedClusterType[]>([]);
|
|
|
const [loading, setLoading] = useState<boolean>(true);
|
|
const [loading, setLoading] = useState<boolean>(true);
|
|
|
const [error, setError] = useState<string>("");
|
|
const [error, setError] = useState<string>("");
|
|
|
- const [currentPage, setCurrentPage] = useState<number>(1); // add a currentPage state, starts at 1
|
|
|
|
|
- const projectsPerPage = 15
|
|
|
|
|
const filteredProjects = useMemo(() => {
|
|
const filteredProjects = useMemo(() => {
|
|
|
const filteredBySearch = projects.filter((project) => {
|
|
const filteredBySearch = projects.filter((project) => {
|
|
|
return project.id === Number(searchValue) || project.name.toLowerCase().includes(searchValue.toLowerCase());
|
|
return project.id === Number(searchValue) || project.name.toLowerCase().includes(searchValue.toLowerCase());
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
- // Get the projects for the current page
|
|
|
|
|
- const startIndex = (currentPage - 1) * projectsPerPage;
|
|
|
|
|
- const endIndex = startIndex + projectsPerPage;
|
|
|
|
|
|
|
+ // sort and return all the projects
|
|
|
|
|
+ const sortedProjects = _.sortBy(filteredBySearch, 'name');
|
|
|
|
|
+
|
|
|
|
|
+ // move the selected project to the top
|
|
|
|
|
+ const selectedProjectIndex = sortedProjects.findIndex(project => project.id === currentProject.id);
|
|
|
|
|
+ if (selectedProjectIndex !== -1) {
|
|
|
|
|
+ const selectedProject = sortedProjects.splice(selectedProjectIndex, 1)[0];
|
|
|
|
|
+ sortedProjects.unshift(selectedProject);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return sortedProjects;
|
|
|
|
|
+ }, [projects, searchValue, currentProject]);
|
|
|
|
|
|
|
|
- return _.sortBy(filteredBySearch, 'name').slice(startIndex, endIndex);
|
|
|
|
|
- }, [projects, searchValue, currentPage]);
|
|
|
|
|
const updateClusterList = async (projectId: number) => {
|
|
const updateClusterList = async (projectId: number) => {
|
|
|
try {
|
|
try {
|
|
|
setLoading(true)
|
|
setLoading(true)
|
|
@@ -70,32 +76,11 @@ const ProjectSelectionModal: React.FC<Props> = ({
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
const renderBlockList = () => {
|
|
const renderBlockList = () => {
|
|
|
- const lastBlock = user && user.isPorterUser ? (
|
|
|
|
|
- <Block
|
|
|
|
|
- isLastBlock={true}
|
|
|
|
|
- key="initialize"
|
|
|
|
|
- onClick={() =>
|
|
|
|
|
- pushFiltered(props, "/new-project", ["project_id"], {
|
|
|
|
|
- new_project: true,
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
- >
|
|
|
|
|
- <BlockTitle>Create a project</BlockTitle>
|
|
|
|
|
- {/* <ProjectIcon>
|
|
|
|
|
- <ProjectImage src={gradient} />
|
|
|
|
|
- <Letter>{"+"}</Letter>
|
|
|
|
|
- </ProjectIcon> */}
|
|
|
|
|
- <BlockDescription>
|
|
|
|
|
- Initialize a new project
|
|
|
|
|
- </BlockDescription>
|
|
|
|
|
- </Block>
|
|
|
|
|
- ) : null;
|
|
|
|
|
-
|
|
|
|
|
return filteredProjects.map((project: ProjectType, i: number) => {
|
|
return filteredProjects.map((project: ProjectType, i: number) => {
|
|
|
return (
|
|
return (
|
|
|
- <Block
|
|
|
|
|
|
|
+ <IdContainer
|
|
|
key={i}
|
|
key={i}
|
|
|
- selected={project.name === currentProject.name}
|
|
|
|
|
|
|
+ selected={project.id === currentProject.id}
|
|
|
onClick={async () => {
|
|
onClick={async () => {
|
|
|
// if (project.id !== currentProject.id) {
|
|
// if (project.id !== currentProject.id) {
|
|
|
// setCurrentCluster(null);
|
|
// setCurrentCluster(null);
|
|
@@ -124,67 +109,46 @@ const ProjectSelectionModal: React.FC<Props> = ({
|
|
|
<BlockDescription>
|
|
<BlockDescription>
|
|
|
Project Id: {project.id}
|
|
Project Id: {project.id}
|
|
|
</BlockDescription>
|
|
</BlockDescription>
|
|
|
- </Block>
|
|
|
|
|
|
|
+ </IdContainer>
|
|
|
);
|
|
);
|
|
|
- }).concat(lastBlock);
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- const renderPaginationButtons = () => {
|
|
|
|
|
- const totalProjects = projects.length;
|
|
|
|
|
- const totalPages = Math.ceil(totalProjects / projectsPerPage);
|
|
|
|
|
-
|
|
|
|
|
- // Only render pagination buttons if there are multiple pages
|
|
|
|
|
- if (totalPages > 1) {
|
|
|
|
|
- return (
|
|
|
|
|
- <PaginationButtonsContainer>
|
|
|
|
|
- <Button
|
|
|
|
|
- disabled={currentPage === 1}
|
|
|
|
|
- onClick={() => setCurrentPage((page) => Math.max(page - 1, 1))}
|
|
|
|
|
- >
|
|
|
|
|
- Previous
|
|
|
|
|
- </Button>
|
|
|
|
|
-
|
|
|
|
|
- <span>{currentPage} / {totalPages}</span>
|
|
|
|
|
-
|
|
|
|
|
- <Button
|
|
|
|
|
- disabled={currentPage === totalPages}
|
|
|
|
|
- onClick={() => setCurrentPage((page) => Math.min(page + 1, totalPages))}
|
|
|
|
|
- >
|
|
|
|
|
- Next
|
|
|
|
|
- </Button>
|
|
|
|
|
- </PaginationButtonsContainer>
|
|
|
|
|
- );
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // If there is only one page, don't render the buttons
|
|
|
|
|
- return null;
|
|
|
|
|
|
|
+ });
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
return (
|
|
return (
|
|
|
- <Modal closeModal={closeModal} width={'900px'}>
|
|
|
|
|
|
|
+ <Modal closeModal={closeModal} width={'600px'}>
|
|
|
<Text size={16} style={{ marginRight: '10px' }}>
|
|
<Text size={16} style={{ marginRight: '10px' }}>
|
|
|
Switch Project
|
|
Switch Project
|
|
|
</Text>
|
|
</Text>
|
|
|
<Spacer y={1} />
|
|
<Spacer y={1} />
|
|
|
|
|
|
|
|
- <SearchBar
|
|
|
|
|
- value={searchValue}
|
|
|
|
|
- setValue={(x) => {
|
|
|
|
|
- setSearchValue(x);
|
|
|
|
|
- }}
|
|
|
|
|
- placeholder="Search projects..."
|
|
|
|
|
- width="100%"
|
|
|
|
|
- />
|
|
|
|
|
|
|
+ <Container row spaced>
|
|
|
|
|
+ <SearchBar
|
|
|
|
|
+ value={searchValue}
|
|
|
|
|
+ setValue={(x) => {
|
|
|
|
|
+ setSearchValue(x);
|
|
|
|
|
+ }}
|
|
|
|
|
+ placeholder="Search projects..."
|
|
|
|
|
+ width="100%"
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <Spacer inline x={1} />
|
|
|
|
|
+
|
|
|
|
|
+ {user.isPorterUser && <Button onClick={() =>
|
|
|
|
|
+ pushFiltered(props, "/new-project", ["project_id"], {
|
|
|
|
|
+ new_project: true,
|
|
|
|
|
+ })} height="30px" width="130px">
|
|
|
|
|
+ <I className="material-icons">add</I> New Project
|
|
|
|
|
+ </Button>}
|
|
|
|
|
+ </Container>
|
|
|
|
|
|
|
|
<Spacer y={1} />
|
|
<Spacer y={1} />
|
|
|
|
|
|
|
|
- <ScrollableContent> {/* Wrap the block list and pagination buttons */}
|
|
|
|
|
- <BlockList>
|
|
|
|
|
|
|
+ <ScrollableContent> {/* Wrap the block list */}
|
|
|
|
|
+ {/* <BlockList>
|
|
|
{renderBlockList()}
|
|
{renderBlockList()}
|
|
|
- </BlockList>
|
|
|
|
|
|
|
+ </BlockList> */}
|
|
|
|
|
+ {renderBlockList()}
|
|
|
<Spacer height="15px" />
|
|
<Spacer height="15px" />
|
|
|
-
|
|
|
|
|
- {renderPaginationButtons()}
|
|
|
|
|
</ScrollableContent>
|
|
</ScrollableContent>
|
|
|
</Modal >
|
|
</Modal >
|
|
|
)
|
|
)
|
|
@@ -192,68 +156,37 @@ const ProjectSelectionModal: React.FC<Props> = ({
|
|
|
|
|
|
|
|
export default withRouter(ProjectSelectionModal);
|
|
export default withRouter(ProjectSelectionModal);
|
|
|
|
|
|
|
|
-const Block = styled.div<{ selected?: boolean }>`
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- user-select: none;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- font-size: 13px;
|
|
|
|
|
- overflow: hidden;
|
|
|
|
|
- font-weight: 500;
|
|
|
|
|
- padding: 3px 0px 12px;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- height: 170px;
|
|
|
|
|
- cursor: pointer;
|
|
|
|
|
- color: #ffffff;
|
|
|
|
|
- position: relative;
|
|
|
|
|
-
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- border-radius: 5px;
|
|
|
|
|
- background: ${props => props.isLastBlock ? '#aaaabb' : props.theme.clickable.bg};
|
|
|
|
|
-
|
|
|
|
|
- border: ${props => props.selected ? "2px solid #8590ff" : "1px solid #494b4f"};
|
|
|
|
|
- :hover {
|
|
|
|
|
- border: ${({ selected }) => (!selected && "1px solid #7a7b80")};
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- animation: fadeIn 0.3s 0s;
|
|
|
|
|
- @keyframes fadeIn {
|
|
|
|
|
- from {
|
|
|
|
|
- opacity: 0;
|
|
|
|
|
- }
|
|
|
|
|
- to {
|
|
|
|
|
- opacity: 1;
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-`;
|
|
|
|
|
|
|
+const IdContainer = styled.div`
|
|
|
|
|
+ color: #ffffff;
|
|
|
|
|
+ border-radius: 5px;
|
|
|
|
|
+ padding: 5px;
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ border-radius: 5px;
|
|
|
|
|
+ border: 1px solid ${({ theme }) => theme.border};
|
|
|
|
|
+ margin-bottom: 10px;
|
|
|
|
|
+ margin-top: 5px;
|
|
|
|
|
+ border: ${props => props.selected ? "2px solid #8590ff" : "1px solid #494b4f"};
|
|
|
|
|
+ :hover {
|
|
|
|
|
+ border: ${({ selected }) => (!selected && "1px solid #7a7b80")};
|
|
|
|
|
+ }
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
|
|
|
-const BlockList = styled.div`
|
|
|
|
|
- overflow: visible;
|
|
|
|
|
- margin-top: 6px;
|
|
|
|
|
- display: grid;
|
|
|
|
|
- grid-column-gap: 25px;
|
|
|
|
|
- grid-row-gap: 25px;
|
|
|
|
|
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
|
|
|
-`;
|
|
|
|
|
|
|
+ animation: fadeIn 0.3s 0s;
|
|
|
|
|
+ @keyframes fadeIn {
|
|
|
|
|
+ from {
|
|
|
|
|
+ opacity: 0;
|
|
|
|
|
+ }
|
|
|
|
|
+ to {
|
|
|
|
|
+ opacity: 1;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ `;
|
|
|
|
|
|
|
|
-const Letter = styled.div`
|
|
|
|
|
- height: 100%;
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- padding-bottom: 2px;
|
|
|
|
|
- font-weight: 10000;
|
|
|
|
|
- font-size: 60px;
|
|
|
|
|
- top: 0;
|
|
|
|
|
- left: 0;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
-`;
|
|
|
|
|
const BlockDescription = styled.div`
|
|
const BlockDescription = styled.div`
|
|
|
color: #ffffff66;
|
|
color: #ffffff66;
|
|
|
margin-left: -10px;
|
|
margin-left: -10px;
|
|
|
|
|
+ margin-top: 4px;
|
|
|
text-align: center;
|
|
text-align: center;
|
|
|
font-weight: default;
|
|
font-weight: default;
|
|
|
font-size: 13px;
|
|
font-size: 13px;
|
|
@@ -277,37 +210,16 @@ const BlockTitle = styled.div`
|
|
|
overflow: hidden;
|
|
overflow: hidden;
|
|
|
text-overflow: ellipsis;
|
|
text-overflow: ellipsis;
|
|
|
`;
|
|
`;
|
|
|
-
|
|
|
|
|
-const Plus = styled.div`
|
|
|
|
|
- margin-right: 10px;
|
|
|
|
|
- font-size: 15px;
|
|
|
|
|
-`;
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-const ProjectImage = styled.img`
|
|
|
|
|
-width: 100%;
|
|
|
|
|
-height: 100%;
|
|
|
|
|
-`;
|
|
|
|
|
-
|
|
|
|
|
-const ProjectIcon = styled.div`
|
|
|
|
|
-width: 75px;
|
|
|
|
|
-min-width: 25px;
|
|
|
|
|
-height: 75px;
|
|
|
|
|
-border-radius: 3px;
|
|
|
|
|
-overflow: hidden;
|
|
|
|
|
-position: relative;
|
|
|
|
|
-margin-right: 10px;
|
|
|
|
|
-font-weight: 400;
|
|
|
|
|
-`;
|
|
|
|
|
-
|
|
|
|
|
-const PaginationButtonsContainer = styled.div`
|
|
|
|
|
- display: flex;
|
|
|
|
|
- justify-content: space-between;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- margin-top: 20px;
|
|
|
|
|
-`;
|
|
|
|
|
const ScrollableContent = styled.div`
|
|
const ScrollableContent = styled.div`
|
|
|
overflow-y: auto; /* Enable vertical scrolling */
|
|
overflow-y: auto; /* Enable vertical scrolling */
|
|
|
- height: calc(100vh - 200px); /* Set the maximum height */
|
|
|
|
|
|
|
+ height: calc(100vh - 500px); /* Set the maximum height */
|
|
|
padding-right: 15px; /* Add some right padding to account for scrollbar */
|
|
padding-right: 15px; /* Add some right padding to account for scrollbar */
|
|
|
`;
|
|
`;
|
|
|
|
|
+const I = styled.i`
|
|
|
|
|
+ color: white;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ margin-right: 5px;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+`;
|