diff --git a/api/src/graphql/parts.sdl.js b/api/src/graphql/parts.sdl.js index c8054a4..917ca7b 100644 --- a/api/src/graphql/parts.sdl.js +++ b/api/src/graphql/parts.sdl.js @@ -16,6 +16,7 @@ export const schema = gql` type Query { parts: [Part!]! part(id: String!): Part + partByUserAndTitle(userName: String! partTitle: String!): Part } input CreatePartInput { diff --git a/api/src/graphql/users.sdl.js b/api/src/graphql/users.sdl.js index 743011f..b714d64 100644 --- a/api/src/graphql/users.sdl.js +++ b/api/src/graphql/users.sdl.js @@ -8,7 +8,8 @@ export const schema = gql` updatedAt: DateTime! image: String bio: String - Part: [Part]! + Parts: [Part]! + Part(partTitle: String!): Part Reaction: [PartReaction]! Comment: [Comment]! } diff --git a/api/src/services/parts/parts.js b/api/src/services/parts/parts.js index 2c3cee1..2460149 100644 --- a/api/src/services/parts/parts.js +++ b/api/src/services/parts/parts.js @@ -11,6 +11,21 @@ export const part = ({ id }) => { where: { id }, }) } +export const partByUserAndTitle = async ({ userName, partTitle }) => { + const user = await db.user.findOne({ + where: { + userName + } + }) + return db.part.findOne({ + where: { + title_userId: { + title: partTitle, + userId: user.id, + } + }, + }) +} export const createPart = async ({ input }) => { return db.part.create({ diff --git a/api/src/services/users/users.js b/api/src/services/users/users.js index 9af1a0e..8ab78f6 100644 --- a/api/src/services/users/users.js +++ b/api/src/services/users/users.js @@ -54,7 +54,11 @@ export const deleteUser = ({ id }) => { } export const User = { - Part: (_obj, { root }) => db.user.findOne({ where: { id: root.id } }).Part(), + Parts: (_obj, { root }) => db.user.findOne({ where: { id: root.id } }).Part(), + Part: (_obj, { root, ...rest }) => db.part.findOne({where: { title_userId: { + title: _obj.partTitle, + userId: root.id, + }}}), Reaction: (_obj, { root }) => db.user.findOne({ where: { id: root.id } }).Reaction(), Comment: (_obj, { root }) => diff --git a/web/src/Routes.js b/web/src/Routes.js index b7a253c..b538756 100644 --- a/web/src/Routes.js +++ b/web/src/Routes.js @@ -17,25 +17,34 @@ const Routes = () => { + + {/* */} + + {/* Ownership enforced routes */} + {/* */} + {/* End ownership enforced routes */} {/* GENERATED ROUTES BELOW, probably going to clean these up and delete most of them, but the CRUD functionality is useful for now */} - - - - - - - - - - - - - - - - + {/* All private by default for safety and because the routes that are left after clean up will probably be admin pages */} + + + + + + + + + + + + + + + + + + ) } diff --git a/web/src/components/Breadcrumb/Breadcrumb.js b/web/src/components/Breadcrumb/Breadcrumb.js new file mode 100644 index 0000000..31f2727 --- /dev/null +++ b/web/src/components/Breadcrumb/Breadcrumb.js @@ -0,0 +1,18 @@ +import { getActiveClasses } from "get-active-classes" + +const Breadcrumb = ({ userName, partTitle, className }) => { + return ( +

+
.
+ + {userName} + +
.
+ + {partTitle} + +

+ ) +} + +export default Breadcrumb diff --git a/web/src/components/Breadcrumb/Breadcrumb.stories.js b/web/src/components/Breadcrumb/Breadcrumb.stories.js new file mode 100644 index 0000000..98b7ef1 --- /dev/null +++ b/web/src/components/Breadcrumb/Breadcrumb.stories.js @@ -0,0 +1,7 @@ +import Breadcrumb from './Breadcrumb' + +export const generated = () => { + return +} + +export default { title: 'Components/Breadcrumb' } diff --git a/web/src/components/Breadcrumb/Breadcrumb.test.js b/web/src/components/Breadcrumb/Breadcrumb.test.js new file mode 100644 index 0000000..adc1e45 --- /dev/null +++ b/web/src/components/Breadcrumb/Breadcrumb.test.js @@ -0,0 +1,11 @@ +import { render } from '@redwoodjs/testing' + +import Breadcrumb from './Breadcrumb' + +describe('Breadcrumb', () => { + it('renders successfully', () => { + expect(() => { + render() + }).not.toThrow() + }) +}) diff --git a/web/src/components/Button/Button.js b/web/src/components/Button/Button.js index c327ec1..0860604 100644 --- a/web/src/components/Button/Button.js +++ b/web/src/components/Button/Button.js @@ -1,16 +1,19 @@ +import { getActiveClasses } from 'get-active-classes' import Svg from 'src/components/Svg' -const Button = ({onClick, iconName, children}) => { +const Button = ({onClick, iconName, children, className, shouldAnimateHover}) => { return ( -
-
) } diff --git a/web/src/components/EmojiReaction/EmojiReaction.js b/web/src/components/EmojiReaction/EmojiReaction.js index 0f2d162..b90151d 100644 --- a/web/src/components/EmojiReaction/EmojiReaction.js +++ b/web/src/components/EmojiReaction/EmojiReaction.js @@ -1,12 +1,19 @@ import { useState } from 'react' -import Fab from '@material-ui/core/Fab' -import IconButton from '@material-ui/core/IconButton' +import { getActiveClasses } from "get-active-classes" import Popover from '@material-ui/core/Popover' + import Svg from 'src/components/Svg' -const emojiMenu = ['🏆', '❤️', '👍', '😊', '😄', '🚀', '👏', '🙌'] +const emojiMenu = ['❤️', '👍', '😄', '🙌'] +// const emojiMenu = ['🏆', '❤️', '👍', '😊', '😄', '🚀', '👏', '🙌'] +const noEmotes =[{ + emoji: '❤️', + count: 0, +}] -const EmojiReaction = ({ emotes, callback = () => {} }) => { +const textShadow = {textShadow: '0 4px 6px rgba(0, 0, 0, 0.3)'} + +const EmojiReaction = ({ emotes, onEmote = () => {}, className }) => { const [isOpen, setIsOpen] = useState(false) const [anchorEl, setAnchorEl] = useState(null) const [popoverId, setPopoverId] = useState(undefined) @@ -32,45 +39,63 @@ const EmojiReaction = ({ emotes, callback = () => {} }) => { } const handleEmojiClick = (emoji) => { - callback(emoji) + onEmote(emoji) closePopover() } - return [ -
- -
- + return ( + <> +
+
+
+ +
- -
- {emotes.map((emote, i) => ( - handleEmojiClick(emote.emoji)}> - {emote.emoji} {emote.count} - - ))} +
+ {(emotes.length ? emotes : noEmotes).map((emote, i) => ( + handleEmojiClick(emote.emoji)} + > + {emote.emoji}{emote.count} + + ))} +
-
, - - {emojiMenu.map((emoji, i) => ( - handleEmojiClick(emoji)}>{emoji} - ))} - , - ] + +
+ {emojiMenu.map((emoji, i) => ( + + ))} +
+
+ + ) } export default EmojiReaction diff --git a/web/src/components/Part2Cell/Part2Cell.js b/web/src/components/Part2Cell/Part2Cell.js new file mode 100644 index 0000000..3314d4a --- /dev/null +++ b/web/src/components/Part2Cell/Part2Cell.js @@ -0,0 +1,110 @@ +import Editor from "rich-markdown-editor"; + +// import Part from 'src/components/Part' +import ImageUploader from 'src/components/ImageUploader' +import Breadcrumb from 'src/components/Breadcrumb' +import EmojiReaction from 'src/components/EmojiReaction' +import Button from 'src/components/Button' + +export const QUERY = gql` + query FIND_PART_BY_USERNAME_TITLE($userName: String!, $partTitle: String!) { + userPart: userName(userName: $userName) { + name + userName + bio + image + id + Part(partTitle: $partTitle) { + id + title + description + code + mainImage + createdAt + updatedAt + userId + } + } + } +` + +export const Loading = () =>
Loading...
+ +export const Empty = () =>
Empty
+ +export const Failure = ({ error }) =>
Error: {error.message}
+ +export const Success = ({ userPart, variables }) => { + return ( + <> +
+ + {/* Side column */} + + + {/* main project center column */} +
+ + { userPart?.Part?.mainImage && {}} + aspectRatio={16/9} + imageUrl={userPart?.Part?.mainImage} + />} +
+ setInput({ + // ...input, + // bio: bioFn(), + // })} + /> +
+
+ +
+ + ) +} diff --git a/web/src/components/Part2Cell/Part2Cell.mock.js b/web/src/components/Part2Cell/Part2Cell.mock.js new file mode 100644 index 0000000..3dde5f5 --- /dev/null +++ b/web/src/components/Part2Cell/Part2Cell.mock.js @@ -0,0 +1,6 @@ +// Define your own mock data here: +export const standard = (/* vars, { ctx, req } */) => ({ + part2: { + id: 42, + }, +}) diff --git a/web/src/components/Part2Cell/Part2Cell.stories.js b/web/src/components/Part2Cell/Part2Cell.stories.js new file mode 100644 index 0000000..8ddfafb --- /dev/null +++ b/web/src/components/Part2Cell/Part2Cell.stories.js @@ -0,0 +1,20 @@ +import { Loading, Empty, Failure, Success } from './Part2Cell' +import { standard } from './Part2Cell.mock' + +export const loading = () => { + return Loading ? : null +} + +export const empty = () => { + return Empty ? : null +} + +export const failure = () => { + return Failure ? : null +} + +export const success = () => { + return Success ? : null +} + +export default { title: 'Cells/Part2Cell' } diff --git a/web/src/components/Part2Cell/Part2Cell.test.js b/web/src/components/Part2Cell/Part2Cell.test.js new file mode 100644 index 0000000..231c8c6 --- /dev/null +++ b/web/src/components/Part2Cell/Part2Cell.test.js @@ -0,0 +1,26 @@ +import { render, screen } from '@redwoodjs/testing' +import { Loading, Empty, Failure, Success } from './Part2Cell' +import { standard } from './Part2Cell.mock' + +describe('Part2Cell', () => { + test('Loading renders successfully', () => { + render() + // Use screen.debug() to see output + expect(screen.getByText('Loading...')).toBeInTheDocument() + }) + + test('Empty renders successfully', async () => { + render() + expect(screen.getByText('Empty')).toBeInTheDocument() + }) + + test('Failure renders successfully', async () => { + render() + expect(screen.getByText(/Oh no/i)).toBeInTheDocument() + }) + + test('Success renders successfully', async () => { + render() + expect(screen.getByText(/42/i)).toBeInTheDocument() + }) +}) diff --git a/web/src/components/UserProfile/UserProfile.js b/web/src/components/UserProfile/UserProfile.js index 925599e..c28ca96 100644 --- a/web/src/components/UserProfile/UserProfile.js +++ b/web/src/components/UserProfile/UserProfile.js @@ -45,9 +45,9 @@ const UserProfile = ({user, isEditable, loading, onSave, error}) => { ...textFields, })} isEditable={isEditable}/> {isEditable ? - : // TODO replace pencil with a save icon + : // TODO replace pencil with a save icon canEdit ? - : + : null }
diff --git a/web/src/pages/Part2Page/Part2Page.js b/web/src/pages/Part2Page/Part2Page.js new file mode 100644 index 0000000..6b4e752 --- /dev/null +++ b/web/src/pages/Part2Page/Part2Page.js @@ -0,0 +1,12 @@ +import MainLayout from 'src/layouts/MainLayout' +import Part2Cell from 'src/components/Part2Cell' + +const Part2Page = ({userName, partTitle}) => { + return ( + + + + ) +} + +export default Part2Page diff --git a/web/src/pages/Part2Page/Part2Page.stories.js b/web/src/pages/Part2Page/Part2Page.stories.js new file mode 100644 index 0000000..621fecf --- /dev/null +++ b/web/src/pages/Part2Page/Part2Page.stories.js @@ -0,0 +1,7 @@ +import Part2Page from './Part2Page' + +export const generated = () => { + return +} + +export default { title: 'Pages/Part2Page' } diff --git a/web/src/pages/Part2Page/Part2Page.test.js b/web/src/pages/Part2Page/Part2Page.test.js new file mode 100644 index 0000000..09857f2 --- /dev/null +++ b/web/src/pages/Part2Page/Part2Page.test.js @@ -0,0 +1,11 @@ +import { render } from '@redwoodjs/testing' + +import Part2Page from './Part2Page' + +describe('Part2Page', () => { + it('renders successfully', () => { + expect(() => { + render() + }).not.toThrow() + }) +})