Allow for new parts to be created
This commit is contained in:
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -17,6 +17,7 @@
|
|||||||
"./web/src/Routes.js",
|
"./web/src/Routes.js",
|
||||||
],
|
],
|
||||||
"cSpell.words": [
|
"cSpell.words": [
|
||||||
"Uploader"
|
"Uploader",
|
||||||
|
"redwoodjs"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export const schema = gql`
|
|||||||
image: String
|
image: String
|
||||||
bio: String
|
bio: String
|
||||||
Parts: [Part]!
|
Parts: [Part]!
|
||||||
Part(partTitle: String!): Part
|
Part(partTitle: String): Part
|
||||||
Reaction: [PartReaction]!
|
Reaction: [PartReaction]!
|
||||||
Comment: [Comment]!
|
Comment: [Comment]!
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { db } from 'src/lib/db'
|
import { db } from 'src/lib/db'
|
||||||
import { requireAuth } from 'src/lib/auth'
|
import { requireAuth } from 'src/lib/auth'
|
||||||
import { requireOwnership } from 'src/lib/owner'
|
import { requireOwnership } from 'src/lib/owner'
|
||||||
|
import { UserInputError } from '@redwoodjs/api'
|
||||||
|
|
||||||
export const users = () => {
|
export const users = () => {
|
||||||
requireAuth({ role: 'admin' })
|
requireAuth({ role: 'admin' })
|
||||||
@@ -43,6 +44,9 @@ export const updateUserByUserName = async ({ userName, input }) => {
|
|||||||
if(input.userName) {
|
if(input.userName) {
|
||||||
input.userName = input.userName.replace(/([^a-zA-Z\d_:])/g, '-')
|
input.userName = input.userName.replace(/([^a-zA-Z\d_:])/g, '-')
|
||||||
}
|
}
|
||||||
|
if(input.userName && ['new', 'edit', 'update'].includes(input.userName)) { //TODO complete this and use a regexp so that it's not case sensitive, don't want someone with the userName eDiT
|
||||||
|
throw new UserInputError(`You've tried to used a protected word as you userName, try something other than `)
|
||||||
|
}
|
||||||
return db.user.update({
|
return db.user.update({
|
||||||
data: input,
|
data: input,
|
||||||
where: { userName },
|
where: { userName },
|
||||||
@@ -58,7 +62,7 @@ export const deleteUser = ({ id }) => {
|
|||||||
|
|
||||||
export const User = {
|
export const User = {
|
||||||
Parts: (_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: {
|
Part: (_obj, { root, ...rest }) => _obj.partTitle && db.part.findOne({where: { title_userId: {
|
||||||
title: _obj.partTitle,
|
title: _obj.partTitle,
|
||||||
userId: root.id,
|
userId: root.id,
|
||||||
}}}),
|
}}}),
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ const Routes = () => {
|
|||||||
<Route notfound page={NotFoundPage} />
|
<Route notfound page={NotFoundPage} />
|
||||||
|
|
||||||
{/* Ownership enforced routes */}
|
{/* Ownership enforced routes */}
|
||||||
|
<Route path="/u/{userName}/new" page={NewPart2Page} name="newPart2" />
|
||||||
<Route path="/u/{userName}/edit" page={EditUser2Page} name="editUser2" />
|
<Route path="/u/{userName}/edit" page={EditUser2Page} name="editUser2" />
|
||||||
<Route path="/u/{userName}/{partTitle}/edit" page={EditPart2Page} name="editPart2" />
|
<Route path="/u/{userName}/{partTitle}/edit" page={EditPart2Page} name="editPart2" />
|
||||||
{/* End ownership enforced routes */}
|
{/* End ownership enforced routes */}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { useAuth } from '@redwoodjs/auth'
|
|||||||
import PartProfile from 'src/components/PartProfile'
|
import PartProfile from 'src/components/PartProfile'
|
||||||
|
|
||||||
export const QUERY = gql`
|
export const QUERY = gql`
|
||||||
query FIND_PART_BY_USERNAME_TITLE($userName: String!, $partTitle: String!, $currentUserId: String) {
|
query FIND_PART_BY_USERNAME_TITLE($userName: String!, $partTitle: String, $currentUserId: String) {
|
||||||
userPart: userName(userName: $userName) {
|
userPart: userName(userName: $userName) {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
@@ -52,6 +52,18 @@ const UPDATE_PART_MUTATION = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
const CREATE_PART_MUTATION = gql`
|
||||||
|
mutation CreatePartMutation($input: CreatePartInput!) {
|
||||||
|
createPart(input: $input) {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
user {
|
||||||
|
id
|
||||||
|
userName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
const TOGGLE_REACTION_MUTATION = gql`
|
const TOGGLE_REACTION_MUTATION = gql`
|
||||||
mutation ToggleReactionMutation($input: TogglePartReactionInput!) {
|
mutation ToggleReactionMutation($input: TogglePartReactionInput!) {
|
||||||
togglePartReaction(input: $input){
|
togglePartReaction(input: $input){
|
||||||
@@ -84,7 +96,17 @@ export const Success = ({ userPart, variables: {isEditable}, refetch}) => {
|
|||||||
addMessage('Part updated.', { classes: 'rw-flash-success' })
|
addMessage('Part updated.', { classes: 'rw-flash-success' })
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
const [createUser] = useMutation(CREATE_PART_MUTATION, {
|
||||||
|
onCompleted: ({createPart}) => {
|
||||||
|
navigate(routes.part2({userName: createPart?.user?.userName, partTitle: createPart?.title}))
|
||||||
|
addMessage('Part Created.', { classes: 'rw-flash-success' })
|
||||||
|
},
|
||||||
|
})
|
||||||
const onSave = (id, input) => {
|
const onSave = (id, input) => {
|
||||||
|
if(!id) {
|
||||||
|
createUser({ variables: { input } })
|
||||||
|
return
|
||||||
|
}
|
||||||
updateUser({ variables: { id, input } })
|
updateUser({ variables: { id, input } })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ const PartProfile = ({
|
|||||||
title: part?.title,
|
title: part?.title,
|
||||||
mainImage: part?.mainImage,
|
mainImage: part?.mainImage,
|
||||||
description: part?.description,
|
description: part?.description,
|
||||||
|
userId: userPart?.id,
|
||||||
})
|
})
|
||||||
const setProperty = (property, value) => setInput({
|
const setProperty = (property, value) => setInput({
|
||||||
...input,
|
...input,
|
||||||
@@ -43,7 +44,7 @@ const PartProfile = ({
|
|||||||
const onImageUpload = ({cloudinaryPublicId}) => setProperty('mainImage', cloudinaryPublicId)
|
const onImageUpload = ({cloudinaryPublicId}) => setProperty('mainImage', cloudinaryPublicId)
|
||||||
const onEditSaveClick = () => {
|
const onEditSaveClick = () => {
|
||||||
if (isEditable) {
|
if (isEditable) {
|
||||||
onSave(part.id, input)
|
input.title && onSave(part?.id, input)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
navigate(routes.editPart2({userName: userPart.userName, partTitle: part.title}))
|
navigate(routes.editPart2({userName: userPart.userName, partTitle: part.title}))
|
||||||
@@ -98,7 +99,7 @@ const PartProfile = ({
|
|||||||
{/* main project center column */}
|
{/* main project center column */}
|
||||||
<section className="col-start-3">
|
<section className="col-start-3">
|
||||||
<Breadcrumb className="inline" onPartTitleChange={isEditable && onTitleChange} userName={userPart.userName} partTitle={input?.title}/>
|
<Breadcrumb className="inline" onPartTitleChange={isEditable && onTitleChange} userName={userPart.userName} partTitle={input?.title}/>
|
||||||
{ input?.mainImage && <ImageUploader
|
{ !!(input?.mainImage || isEditable) && <ImageUploader
|
||||||
className="rounded-lg shadow-md border-2 border-gray-200 border-solid mt-8"
|
className="rounded-lg shadow-md border-2 border-gray-200 border-solid mt-8"
|
||||||
onImageUpload={onImageUpload}
|
onImageUpload={onImageUpload}
|
||||||
aspectRatio={16/9}
|
aspectRatio={16/9}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useAuth } from '@redwoodjs/auth'
|
|||||||
import { Flash } from '@redwoodjs/web'
|
import { Flash } from '@redwoodjs/web'
|
||||||
import Tooltip from '@material-ui/core/Tooltip';
|
import Tooltip from '@material-ui/core/Tooltip';
|
||||||
import { useQuery } from '@redwoodjs/web'
|
import { useQuery } from '@redwoodjs/web'
|
||||||
|
import {getActiveClasses} from 'get-active-classes'
|
||||||
|
|
||||||
export const QUERY = gql`
|
export const QUERY = gql`
|
||||||
query FIND_USER_BY_ID($id: String!) {
|
query FIND_USER_BY_ID($id: String!) {
|
||||||
@@ -44,10 +45,10 @@ const MainLayout = ({ children }) => {
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul className="flex items-center">
|
<ul className="flex items-center">
|
||||||
<li className="mr-8 h-10 w-10 rounded-full border-2 border-indigo-300 flex items-center justify-center">
|
<li className={getActiveClasses("mr-8 h-10 w-10 rounded-full border-2 border-gray-700 flex items-center justify-center", {'border-indigo-300': currentUser})}>
|
||||||
{/* <Link to={routes.newPart()}> */}
|
<button className="h-full w-full" onClick={() => currentUser && routes.newPart2({userName: data?.user?.userName})}>
|
||||||
<Svg name="plus" className="text-indigo-300 w-full h-full" />
|
<Svg name="plus" className={getActiveClasses("text-gray-700 w-full h-full",{'text-indigo-300': currentUser})} />
|
||||||
{/* </Link> */}
|
</button>
|
||||||
</li>
|
</li>
|
||||||
{
|
{
|
||||||
isAuthenticated ?
|
isAuthenticated ?
|
||||||
|
|||||||
20
web/src/pages/NewPart2Page/NewPart2Page.js
Normal file
20
web/src/pages/NewPart2Page/NewPart2Page.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { useAuth } from '@redwoodjs/auth'
|
||||||
|
|
||||||
|
import MainLayout from 'src/layouts/MainLayout'
|
||||||
|
import Part2Cell from 'src/components/Part2Cell'
|
||||||
|
|
||||||
|
|
||||||
|
const NewPart2Page = ({userName}) => {
|
||||||
|
const { currentUser } = useAuth()
|
||||||
|
return (
|
||||||
|
<MainLayout>
|
||||||
|
<Part2Cell
|
||||||
|
userName={userName}
|
||||||
|
currentUserId={currentUser?.sub}
|
||||||
|
isEditable
|
||||||
|
/>
|
||||||
|
</MainLayout>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NewPart2Page
|
||||||
7
web/src/pages/NewPart2Page/NewPart2Page.stories.js
Normal file
7
web/src/pages/NewPart2Page/NewPart2Page.stories.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import NewPart2Page from './NewPart2Page'
|
||||||
|
|
||||||
|
export const generated = () => {
|
||||||
|
return <NewPart2Page />
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { title: 'Pages/NewPart2Page' }
|
||||||
11
web/src/pages/NewPart2Page/NewPart2Page.test.js
Normal file
11
web/src/pages/NewPart2Page/NewPart2Page.test.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { render } from '@redwoodjs/testing'
|
||||||
|
|
||||||
|
import NewPart2Page from './NewPart2Page'
|
||||||
|
|
||||||
|
describe('NewPart2Page', () => {
|
||||||
|
it('renders successfully', () => {
|
||||||
|
expect(() => {
|
||||||
|
render(<NewPart2Page />)
|
||||||
|
}).not.toThrow()
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user