Skip to content

Commit 429269a

Browse files
committed
publish functional
1 parent 20436e1 commit 429269a

File tree

8 files changed

+174
-17
lines changed

8 files changed

+174
-17
lines changed

app/src/components/top/NavBar.tsx

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,21 @@ import Avatar from '@mui/material/Avatar';
44
import Button from '@mui/material/Button';
55
import MoreVertIcon from '@mui/icons-material/MoreVert';
66
import NavBarButtons from './NavBarButtons';
7-
import NavbarDropDown from './NavBarButtons';
87
import NewExportButton from './NewExportButton';
98
import { RootState } from '../../redux/store';
109
import logo from '../../public/icons/win/logo.png';
11-
import { useSelector } from 'react-redux';
10+
import { useSelector, useDispatch } from 'react-redux';
11+
import { publishProject } from '../../helperFunctions/projectGetSaveDel';
12+
import PublishModal from './PublishModal';
13+
1214

1315
const NavBar = () => {
1416
const [dropMenu, setDropMenu] = useState(false);
17+
const state = useSelector((store: RootState) => store.appState);
18+
const [publishModalOpen, setPublishModalOpen] = useState(false);
19+
const [projectName, setProjectName] = useState(state.name || '');
20+
const [invalidProjectName, setInvalidProjectName] = useState(false);
21+
const [invalidProjectNameMessage, setInvalidProjectNameMessage] = useState('');
1522
const isDarkMode = useSelector(
1623
(state: RootState) => state.darkMode.isDarkMode
1724
);
@@ -49,6 +56,25 @@ const NavBar = () => {
4956
marginRight: '10px'
5057
};
5158

59+
const handlePublish = () => {
60+
if (state.isLoggedIn === true && projectName === '') {
61+
setInvalidProjectName(true);
62+
setPublishModalOpen(true);
63+
return;
64+
}
65+
66+
if (state.name === '') {
67+
publishProject(state, projectName)
68+
.then(() => {
69+
console.log('Project published successfully');
70+
setPublishModalOpen(false);
71+
})
72+
.catch((error) => {
73+
console.error('Error publishing project:', error.message);
74+
});
75+
}
76+
};
77+
5278
return (
5379
<nav
5480
className="main-navbar"
@@ -59,15 +85,17 @@ const NavBar = () => {
5985
}
6086
>
6187
<Link to="/" style={{ textDecoration: 'none' }}>
62-
<div className="main-logo">
63-
<Avatar src={logo}></Avatar>
64-
<h1 style={isDarkMode ? { color: 'white' } : { color: 'white' }}>
65-
ReacType
66-
</h1>
67-
</div>
68-
</Link>
88+
<div className="main-logo">
89+
<Avatar src={logo}></Avatar>
90+
<h1 style={isDarkMode ? { color: 'white' } : { color: 'white' }}>
91+
ReacType
92+
</h1>
93+
</div>
94+
</Link>
6995
<div style={buttonContainerStyle}>
70-
<button style={buttonStyle}>Share</button>
96+
<button style={buttonStyle} onClick={handlePublish}>
97+
Publish
98+
</button>
7199
<NewExportButton />
72100
<Button
73101
style={moreVertButtonStyle}
@@ -83,6 +111,15 @@ const NavBar = () => {
83111
style={{ color: 'white' }}
84112
/>
85113
</div>
114+
<PublishModal
115+
open={publishModalOpen}
116+
onClose={() => setPublishModalOpen(false)}
117+
onSave={handlePublish}
118+
projectName={projectName}
119+
onChange={(e) => setProjectName(e.target.value)}
120+
invalidProjectName={invalidProjectName}
121+
invalidProjectNameMessage={invalidProjectNameMessage}
122+
/>
86123
</nav>
87124
);
88125
};

app/src/components/top/NavBarButtons.tsx

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { useDispatch, useSelector } from 'react-redux';
2-
32
import { Button } from '@mui/material';
43
import DeleteProjects from '../right/DeleteProjects';
54
import ExportButton from '../right/ExportButton';
@@ -12,7 +11,7 @@ import MarketplaceButton from '../right/MarketplaceButton';
1211
import Menu from '@mui/material/Menu';
1312
import MenuItem from '@mui/material/MenuItem';
1413
import ProjectsFolder from '../right/OpenProjects';
15-
import React from 'react';
14+
import React, { useEffect, useRef } from 'react';
1615
import { RootState } from '../../redux/store';
1716
import SaveProjectButton from '../right/SaveProjectButton';
1817
import { allCooperativeState } from '../../redux/reducers/slice/appStateSlice';
@@ -185,6 +184,8 @@ const StyledMenuItem = withStyles((theme) => ({
185184
// where the main function starts //
186185
function navbarDropDown(props) {
187186
const dispatch = useDispatch();
187+
const dropdownRef = useRef(null);
188+
188189
const [modal, setModal] = React.useState(null);
189190
const [anchorEl, setAnchorEl] = React.useState(null);
190191
const [roomCode, setRoomCode] = React.useState('');
@@ -201,6 +202,23 @@ function navbarDropDown(props) {
201202
setAnchorEl(event.currentTarget);
202203
};
203204

205+
useEffect(() => {
206+
function handleClickOutside(event) {
207+
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
208+
props.setDropMenu(false);
209+
}
210+
}
211+
212+
// Attach the event listener
213+
document.addEventListener("mousedown", handleClickOutside);
214+
215+
// Clean up the event listener
216+
return () => {
217+
document.removeEventListener("mousedown", handleClickOutside);
218+
};
219+
}, [props]);
220+
221+
204222
const clearWorkspace = () => {
205223
// Reset state for project to initial state
206224
const resetState = () => {
@@ -295,7 +313,7 @@ function navbarDropDown(props) {
295313
const showMenu = props.dropMenu ? 'navDropDown' : 'hideNavDropDown';
296314

297315
return (
298-
<div className={showMenu}>
316+
<div ref={dropdownRef} className={showMenu}>
299317
<Link to="/tutorial" style={{ textDecoration: 'none' }} target="_blank">
300318
<button>
301319
Tutorial
@@ -385,4 +403,4 @@ function navbarDropDown(props) {
385403
);
386404
}
387405

388-
export default navbarDropDown;
406+
export default navbarDropDown;
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import React from 'react';
2+
import Dialog from '@mui/material/Dialog';
3+
import DialogTitle from '@mui/material/DialogTitle';
4+
import DialogContent from '@mui/material/DialogContent';
5+
import DialogActions from '@mui/material/DialogActions';
6+
import Button from '@mui/material/Button';
7+
import TextField from '@mui/material/TextField';
8+
9+
const PublishModal = ({
10+
open,
11+
onClose,
12+
onSave,
13+
projectName,
14+
onChange,
15+
invalidProjectName,
16+
invalidProjectNameMessage,
17+
}) => {
18+
return (
19+
<Dialog
20+
style={{ color: "#000" }}
21+
open={open}
22+
onClose={onClose}
23+
aria-labelledby="form-dialog-title"
24+
>
25+
<DialogTitle style={{ color: "#000" }} id="form-dialog-title">Publish Project</DialogTitle>
26+
<DialogContent>
27+
<TextField
28+
autoFocus
29+
inputProps={{ style: { color: "black" } }}
30+
margin="dense"
31+
id="name"
32+
label="Project Name"
33+
type="text"
34+
fullWidth
35+
value={projectName}
36+
onChange={onChange}
37+
helperText={invalidProjectNameMessage}
38+
error={invalidProjectName}
39+
/>
40+
</DialogContent>
41+
<DialogActions>
42+
<Button onClick={onClose} color="primary">
43+
Cancel
44+
</Button>
45+
<Button onClick={onSave} color="primary">
46+
Publish
47+
</Button>
48+
</DialogActions>
49+
</Dialog>
50+
);
51+
};
52+
53+
export default PublishModal;

app/src/containers/MarketplaceContainer.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ const MarketplaceContainer = () => {
3434

3535
};
3636

37-
3837
return (
3938
<div style={containerStyles}>
4039
<div style={contentStyles}>

app/src/helperFunctions/projectGetSaveDel.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { State } from "../interfaces/Interfaces";
2+
13
const isDev = process.env.NODE_ENV === 'development';
24
const { DEV_PORT, API_BASE_URL } = require('../../../config.js');
35
let serverURL = API_BASE_URL;
@@ -54,6 +56,42 @@ export const saveProject = (
5456
return project;
5557
};
5658

59+
export const publishProject = (
60+
projectData: State,
61+
projectName: string
62+
): Promise<Object> => {
63+
const body = JSON.stringify({
64+
_id: projectData._id,
65+
project: { ...projectData, name: projectName },
66+
userId: window.localStorage.getItem('ssid'),
67+
username: window.localStorage.getItem('username'),
68+
comments: [],
69+
name: projectName,
70+
});
71+
72+
const response = fetch(`${serverURL}/publishProject`, {
73+
method: 'POST',
74+
headers: {
75+
'Content-Type': 'application/json',
76+
},
77+
credentials: 'include',
78+
body
79+
});
80+
81+
const publishedProject = response
82+
.then((res) => res.json())
83+
.then((data) => {
84+
return data.project;
85+
})
86+
.catch((err) => {
87+
console.log(`Error publishing project ${err}`);
88+
throw err;
89+
});
90+
91+
return publishedProject;
92+
};
93+
94+
5795
export const deleteProject = (project: any): Promise<Object> => {
5896
const body = JSON.stringify({
5997
name: project.name,

app/src/interfaces/Interfaces.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { DragObjectWithType } from 'react-dnd';
33
export interface State {
44
name: string;
55
forked: boolean;
6+
_id: string;
67
isLoggedIn: boolean;
78
components: Component[];
89
rootComponents: number[];
@@ -130,6 +131,12 @@ export interface Attributes {
130131
compLink?: string;
131132
}
132133

134+
// interface PublishResponse {
135+
// success: boolean;
136+
// error?: string;
137+
// }
138+
139+
133140
export interface Arrow {
134141
renderArrow: (id: number) => any;
135142
deleteLines: () => void;

app/src/redux/reducers/slice/appStateSlice.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import manageSeparators from '../../../helperFunctions/manageSeparators';
1414
export const initialState: State = {
1515
name: '',
1616
forked: false,
17+
_id: '',
1718
isLoggedIn: false,
1819
// config: { saveFlag: true, saveTimer: false },
1920
components: [

server/controllers/projectController.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const projectController: ProjectController = {
3939
// gets all of current user's projects
4040
getProjects: (req, res, next) => {
4141
const { userId } = req.body;
42-
Projects.find({ userId }, (err, projects: Projects) => {
42+
Projects.find({ userId }, (err, projects: Array<{_id: string; project: any }>) => {
4343
if (err) {
4444
return next({
4545
log: `Error in projectController.getProjects: ${err}`,
@@ -49,11 +49,15 @@ const projectController: ProjectController = {
4949
});
5050
}
5151
// so it returns each project like it is in state, not the whole object in DB
52-
res.locals.projects = projects.map((elem) => elem.project);
52+
res.locals.projects = projects.map((elem) =>({
53+
_id: elem._id,
54+
...elem.project
55+
}));
5356
return next();
5457
});
5558
},
5659

60+
5761
// delete project from database **currently not integrated into app**
5862
deleteProject: (req, res, next) => {
5963
// pull project name and userId from req.body

0 commit comments

Comments
 (0)