Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Edit Jam page #70

Merged
merged 12 commits into from
Aug 31, 2021
25 changes: 25 additions & 0 deletions components/ComponentSwitcher.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { cloneElement } from 'react';
import { useState } from 'react';

const ComponentSwitcher = ({
primaryComponent,
secondaryComponent,
}) => {
const [isSwitched, setIsSwitched] = useState(false);

const invertComponent = () => {
setIsSwitched((isSwitched) => !isSwitched);
};

if (isSwitched) {
return cloneElement(secondaryComponent, {
invertComponent: invertComponent,
});
} else {
return cloneElement(primaryComponent, {
invertComponent: invertComponent,
});
}
};

export default ComponentSwitcher;
155 changes: 155 additions & 0 deletions components/ModeratorNewStatementCard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import {
Box,
Button,
Flex,
HStack,
Spacer,
Stack,
Text,
Textarea,
} from '@chakra-ui/react';
import { ChatIcon } from '@chakra-ui/icons';
import { useState } from 'react';
import ComponentSwitcher from './ComponentSwitcher';

const ModeratorEditStatementCard = ({
statement,
jamId,
patchRequest,
invertComponent,
}) => {
const [editedStatement, setEditedStatement] = useState(
statement.text,
);

const handleStatementChange = (e) => {
let statementValue = e.target.value;
setEditedStatement(statementValue);
};

return (
<Box
border="1px"
p={5}
borderRadius="md"
borderColor="gray.200"
my={4}
backgroundColor="white"
>
<Textarea
defaultValue={statement.text}
onChange={handleStatementChange}
borderRadius="none"
mb={4}
></Textarea>
<Stack justify="flex-end" direction="row" spacing={2}>
<Button onClick={() => invertComponent()} variant="outline">
Cancel
</Button>
<Button
onClick={() => {
patchRequest({
text: editedStatement,
jamId: jamId,
statementId: statement.key,
});
invertComponent();
}}
colorScheme="blue"
>
Save
</Button>
</Stack>
</Box>
);
};

const ModeratorDecisionStatementCard = ({
statement,
jamId,
patchRequest,
invertComponent,
}) => {
return (
<Box
border="1px"
p={5}
borderRadius="md"
borderColor="gray.200"
my={4}
backgroundColor="white"
>
<Text pb={5}>{statement.text}</Text>

<Flex>
<Box>
<Text color="gray.600">
<ChatIcon></ChatIcon> Participant submitted{' '}
siame marked this conversation as resolved.
Show resolved Hide resolved
{new Date(
statement.createdAt?._seconds * 1000,
).toUTCString()}
</Text>
</Box>

<Spacer />

<HStack spacing={2}>
<Button onClick={() => invertComponent()} variant="outline">
Edit
</Button>
<Button
onClick={() =>
patchRequest({
state: -1,
jamId: jamId,
statementId: statement.key,
})
}
colorScheme="blue"
>
Reject
</Button>
<Button
onClick={() =>
patchRequest({
state: 1,
jamId: jamId,
statementId: statement.key,
})
}
colorScheme="blue"
>
Approve
</Button>
</HStack>
</Flex>
</Box>
);
};

const ModeratorNewStatementCard = ({
statement,
jamId,
patchRequest,
}) => {
return (
<ComponentSwitcher
primaryComponent={
<ModeratorDecisionStatementCard
statement={statement}
jamId={jamId}
patchRequest={patchRequest}
/>
}
secondaryComponent={
<ModeratorEditStatementCard
statement={statement}
jamId={jamId}
patchRequest={patchRequest}
/>
}
/>
);
};

export default ModeratorNewStatementCard;
2 changes: 1 addition & 1 deletion components/OverviewJamCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const overviewCard = ({
borderColor="#8D8D8D"
>
<Stack>
<Link href={`jams/${jamUrl}/edit`}>
<Link href={`moderator/${jamUrl}`}>
<Heading
fontSize={'18px'}
lineHeight={'26px'}
Expand Down
55 changes: 23 additions & 32 deletions components/ProposedStatementCard.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Text, Button, Textarea, Stack, Box } from '@chakra-ui/react';
import { useState } from 'react';
import ComponentSwitcher from './ComponentSwitcher';

const EditableStatement = ({
children,
index,
onSave,
invertEditable,
invertComponent,
}) => {
const [statement, setStatement] = useState(children);

Expand All @@ -24,13 +25,13 @@ const EditableStatement = ({
borderRadius="none"
/>
<Stack justify="flex-end" direction="row" spacing={2}>
<Button onClick={() => invertEditable()} variant="outline">
<Button onClick={() => invertComponent()} variant="outline">
Cancel
</Button>
<Button
onClick={() => {
onSave(index, statement);
invertEditable();
invertComponent();
}}
variant="outline"
>
Expand All @@ -45,7 +46,7 @@ const VisibleOnlyStatement = ({
children,
index,
onDelete,
invertEditable,
invertComponent,
}) => {
return (
<Box w="100%" border="1px" p={3} borderRadius="md" mb={3}>
Expand All @@ -55,7 +56,7 @@ const VisibleOnlyStatement = ({
<Button onClick={() => onDelete(index)} variant="outline">
Delete
</Button>
<Button onClick={() => invertEditable()} variant="outline">
<Button onClick={() => invertComponent()} variant="outline">
Edit
</Button>
</Stack>
Expand All @@ -64,33 +65,23 @@ const VisibleOnlyStatement = ({
};

function ProposedStatementCard(props) {
const [isEditable, setIsEditable] = useState(false);

const invertEditable = () => {
setIsEditable((isEditable) => !isEditable);
};

if (isEditable) {
return (
<EditableStatement
index={props.index}
onSave={props.onSave}
invertEditable={invertEditable}
>
{props.children}
</EditableStatement>
);
} else {
return (
<VisibleOnlyStatement
index={props.index}
onDelete={props.onDelete}
invertEditable={invertEditable}
>
{props.children}
</VisibleOnlyStatement>
);
}
return (
<ComponentSwitcher
primaryComponent={
<VisibleOnlyStatement
index={props.index}
onDelete={props.onDelete}
>
{props.children}
</VisibleOnlyStatement>
}
secondaryComponent={
<EditableStatement index={props.index} onSave={props.onSave}>
{props.children}
</EditableStatement>
}
/>
);
}

export default ProposedStatementCard;
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"test": "jest"
},
"dependencies": {
"@chakra-ui/icons": "^1.0.15",
"@chakra-ui/react": "^1.6.6",
"@emotion/react": "^11",
"@emotion/styled": "^11",
Expand Down
66 changes: 54 additions & 12 deletions pages/api/jam.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import fire from '../../config/firebaseAdminConfig';
import ensureAdmin from 'utils/admin-auth-middleware';

function getJamByUrlPath(jamUrlPath) {
async function getJamByUrlPath(jamUrlPath, includeStatements) {
const db = fire.firestore();
const jamsRef = db.collection('jams');

return jamsRef
const finalJam = await jamsRef
.where('urlPath', '==', jamUrlPath)
.get()
.then((querySnapshot) => {
Expand All @@ -17,6 +17,26 @@ function getJamByUrlPath(jamUrlPath) {
});
return jams[0];
});

if (!includeStatements) {
return finalJam;
}

finalJam.statements = await jamsRef
.doc(finalJam.key)
.collection('statements')
.get()
.then((query) => {
const statements = [];
query.forEach((doc) => {
const statement = doc.data();
statement.key = doc.id;
statements.push(statement);
});
return statements;
});

return finalJam;
}

function createJam({ name, description, statements }) {
Expand Down Expand Up @@ -59,9 +79,27 @@ function createJam({ name, description, statements }) {
});
}

function patchJam(req, res) {
const { jamId, ...body } = req.body;
const db = fire.firestore();
const jamsRef = db.collection('jams');

return new Promise(() => {
jamsRef
.doc(jamId)
.update(body)
.then(() => {
res.status(200).end();
})
.catch((error) => {
console.error('Error writing document: ', error);
});
});
}

export default async function handler(req, res) {
const {
query: { jamUrlPath },
query: { jamUrlPath, includeStatements },
method,
} = req;

Expand Down Expand Up @@ -99,14 +137,18 @@ export default async function handler(req, res) {
res.status(500).json({ error: error });
}
} else if (method === 'GET') {
return getJamByUrlPath(jamUrlPath).then((jam) => {
if (jam) {
res.status(200);
res.setHeader('Content-Type', 'application/json');
res.json(jam);
} else {
res.status(404).end();
}
});
return getJamByUrlPath(jamUrlPath, includeStatements).then(
(jam) => {
if (jam) {
res.status(200);
res.setHeader('Content-Type', 'application/json');
res.json(jam);
} else {
res.status(404).end();
}
},
);
} else if (method === 'PATCH') {
return patchJam(req, res);
}
}
Loading