Skip to content

Commit f60155a

Browse files
committed
Migrate latest project-persistence features to python editor
1 parent 908d9d9 commit f60155a

File tree

10 files changed

+476
-93
lines changed

10 files changed

+476
-93
lines changed

src/ProjectPageRouting.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ interface ProjectPageRoutingProps {
77
children: ReactNode;
88
}
99
const ProjectPageRouting = ({ children }: ProjectPageRoutingProps) => {
10-
const [{ tab }, navigate] = useRouterState();
10+
const [{ tab }] = useRouterState();
1111
const { projectId, restoreMostRecentProject } = useProjectStorage();
1212

1313
useEffect(() => {
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { Modal, ModalOverlay, ModalContent, ModalHeader, ModalCloseButton, ModalBody, VStack, List, ListItem, Heading, Button, ModalFooter } from "@chakra-ui/react";
2+
import { HistoryList } from "./project-history-db";
3+
import { ProjectEntry } from "./project-list-db";
4+
import { useEffect, useState } from "react";
5+
import { useProjectStorage } from "./ProjectStorageProvider";
6+
import { significantDateUnits } from "./utils";
7+
8+
interface ProjectHistoryModalProps {
9+
onLoadRequest: (projectId: string, revisionId: string) => void;
10+
isOpen: boolean;
11+
onDismiss: () => void;
12+
projectInfo: ProjectEntry | null;
13+
}
14+
15+
const ProjectHistoryModal = ({
16+
onLoadRequest,
17+
isOpen,
18+
onDismiss,
19+
projectInfo
20+
}: ProjectHistoryModalProps) => {
21+
const [projectHistoryList, setProjectHistoryList] = useState<HistoryList | null>(null);
22+
const {getHistory, saveRevision} = useProjectStorage();
23+
24+
const getProjectHistory = async () => {
25+
if (projectInfo === null) {
26+
setProjectHistoryList(null);
27+
return;
28+
}
29+
const historyList = await getHistory(projectInfo.id);
30+
setProjectHistoryList(historyList.sort(h => -h.timestamp));
31+
};
32+
33+
useEffect(() => {
34+
void getProjectHistory();
35+
}, [projectInfo]);
36+
37+
return (<Modal
38+
isOpen={isOpen && !!projectInfo}
39+
onClose={onDismiss}
40+
>
41+
<ModalOverlay />
42+
<ModalContent maxHeight="80dvh" width="50dvw" maxWidth="unset">
43+
<ModalHeader>Project history</ModalHeader>
44+
<ModalCloseButton />
45+
<ModalBody overflowY="auto">
46+
{projectInfo && (
47+
<VStack>
48+
<Heading as="h3">{projectInfo.projectName}</Heading>
49+
<List>
50+
<ListItem key="projectHead" fontSize="lg" pt={4}>
51+
<Heading as="h5">Latest</Heading>
52+
<Button
53+
onClick={async () => {
54+
await saveRevision(projectInfo);
55+
await getProjectHistory();
56+
}}
57+
>
58+
Save as new revision
59+
</Button>
60+
</ListItem>
61+
{projectHistoryList?.map((ph) => (
62+
<ListItem key={ph.revisionId} fontSize="lg" pt={4}>
63+
<Heading as="h5">
64+
Saved on {significantDateUnits(new Date(ph.timestamp))}
65+
</Heading>
66+
<Button
67+
onClick={() => onLoadRequest(ph.projectId, ph.revisionId)}
68+
>
69+
Load as new project
70+
</Button>
71+
</ListItem>
72+
))}
73+
</List>
74+
</VStack>)}
75+
</ModalBody>
76+
77+
<ModalFooter>
78+
<Button
79+
colorScheme="blue"
80+
mr={3}
81+
onClick={onDismiss}
82+
>
83+
Close
84+
</Button>
85+
</ModalFooter>
86+
</ModalContent>
87+
</Modal>)
88+
}
89+
90+
export default ProjectHistoryModal;
Lines changed: 74 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
1-
import { CloseButton, GridItem, Heading, HStack, Text } from "@chakra-ui/react";
1+
import {
2+
CloseButton,
3+
GridItem,
4+
Heading,
5+
HStack,
6+
IconButton,
7+
Text,
8+
} from "@chakra-ui/react";
29
import { ReactNode } from "react";
310
import { ProjectEntry } from "./project-list-db";
411
import { timeAgo } from "./utils";
12+
import { RiEditFill, RiHistoryFill } from "react-icons/ri";
513

614
interface ProjectItemProps {
7-
project: ProjectEntry;
8-
loadProject: (projectId: string) => void;
9-
deleteProject: (projectId: string) => void;
15+
project: ProjectEntry;
16+
showHistory: (projectId: string) => void;
17+
loadProject: (projectId: string) => void;
18+
deleteProject: (projectId: string) => void;
19+
renameProject: (projectId: string) => void;
1020
}
1121

1222
interface ProjectItemBaseProps {
@@ -37,38 +47,68 @@ const ProjectItemBase = ({ onClick, children }: ProjectItemBaseProps) => (
3747
</GridItem>
3848
);
3949

40-
export const ProjectItem = ({project, loadProject, deleteProject}: ProjectItemProps) => (
41-
<ProjectItemBase onClick={() => loadProject(project.id)}>
42-
<HStack justifyContent="space-between" w="100%">
43-
<Heading as="h2" isTruncated>
44-
{project.projectName}
45-
</Heading>
46-
</HStack>
47-
<Text size="lg">{timeAgo(new Date(project.modifiedDate))}</Text>
50+
export const ProjectItem = ({
51+
project,
52+
loadProject,
53+
deleteProject,
54+
renameProject,
55+
showHistory,
56+
}: ProjectItemProps) => (
57+
<ProjectItemBase onClick={() => loadProject(project.id)}>
58+
<HStack justifyContent="space-between" w="100%">
59+
<Heading as="h2" isTruncated>
60+
{project.projectName}
61+
</Heading>
62+
</HStack>
63+
<Text size="lg">{timeAgo(new Date(project.modifiedDate))}</Text>
4864

49-
<CloseButton
50-
position="absolute"
51-
right={4}
52-
top={4}
53-
onClick={(e) => {
54-
deleteProject(project.id);
55-
e.stopPropagation();
56-
e.preventDefault();
57-
}}
58-
/>
59-
</ProjectItemBase>
60-
)
65+
<IconButton
66+
aria-label="Project history"
67+
icon={<RiHistoryFill />}
68+
mr="2"
69+
onClick={(e) => {
70+
showHistory(project.id);
71+
e.stopPropagation();
72+
e.preventDefault();
73+
}}
74+
size="lg"
75+
title="Project history"
76+
variant="outline"
77+
/>
78+
<IconButton
79+
aria-label="Rename"
80+
icon={<RiEditFill />}
81+
onClick={(e) => {
82+
renameProject(project.id);
83+
e.stopPropagation();
84+
e.preventDefault();
85+
}}
86+
size="lg"
87+
title="Rename"
88+
variant="outline"
89+
/>
90+
<CloseButton
91+
position="absolute"
92+
right={4}
93+
top={4}
94+
onClick={(e) => {
95+
deleteProject(project.id);
96+
e.stopPropagation();
97+
e.preventDefault();
98+
}}
99+
/>
100+
</ProjectItemBase>
101+
);
61102

62103
interface AddProjectItemProps {
63-
newProject: () => void;
104+
newProject: () => void;
64105
}
65106

66-
export const AddProjectItem = ({newProject}: AddProjectItemProps) =>
67-
<ProjectItemBase
68-
onClick={newProject}
69-
>
70-
<HStack justifyContent="space-between" w="100%">
71-
<Heading as="h2">New project</Heading>
72-
</HStack>
73-
<Text size="lg">Click to create</Text>
74-
</ProjectItemBase>
107+
export const AddProjectItem = ({ newProject }: AddProjectItemProps) => (
108+
<ProjectItemBase onClick={newProject}>
109+
<HStack justifyContent="space-between" w="100%">
110+
<Heading as="h2">New project</Heading>
111+
</HStack>
112+
<Text size="lg">Click to create</Text>
113+
</ProjectItemBase>
114+
);

0 commit comments

Comments
 (0)