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",
|
||||
],
|
||||
"cSpell.words": [
|
||||
"Uploader"
|
||||
"Uploader",
|
||||
"redwoodjs"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ export const schema = gql`
|
||||
image: String
|
||||
bio: String
|
||||
Parts: [Part]!
|
||||
Part(partTitle: String!): Part
|
||||
Part(partTitle: String): Part
|
||||
Reaction: [PartReaction]!
|
||||
Comment: [Comment]!
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { db } from 'src/lib/db'
|
||||
import { requireAuth } from 'src/lib/auth'
|
||||
import { requireOwnership } from 'src/lib/owner'
|
||||
import { UserInputError } from '@redwoodjs/api'
|
||||
|
||||
export const users = () => {
|
||||
requireAuth({ role: 'admin' })
|
||||
@@ -43,6 +44,9 @@ export const updateUserByUserName = async ({ userName, input }) => {
|
||||
if(input.userName) {
|
||||
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({
|
||||
data: input,
|
||||
where: { userName },
|
||||
@@ -58,7 +62,7 @@ export const deleteUser = ({ id }) => {
|
||||
|
||||
export const User = {
|
||||
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,
|
||||
userId: root.id,
|
||||
}}}),
|
||||
|
||||
@@ -17,6 +17,7 @@ const Routes = () => {
|
||||
<Route notfound page={NotFoundPage} />
|
||||
|
||||
{/* 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}/{partTitle}/edit" page={EditPart2Page} name="editPart2" />
|
||||
{/* End ownership enforced routes */}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useAuth } from '@redwoodjs/auth'
|
||||
import PartProfile from 'src/components/PartProfile'
|
||||
|
||||
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) {
|
||||
id
|
||||
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`
|
||||
mutation ToggleReactionMutation($input: TogglePartReactionInput!) {
|
||||
togglePartReaction(input: $input){
|
||||
@@ -84,7 +96,17 @@ export const Success = ({ userPart, variables: {isEditable}, refetch}) => {
|
||||
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) => {
|
||||
if(!id) {
|
||||
createUser({ variables: { input } })
|
||||
return
|
||||
}
|
||||
updateUser({ variables: { id, input } })
|
||||
}
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ const PartProfile = ({
|
||||
title: part?.title,
|
||||
mainImage: part?.mainImage,
|
||||
description: part?.description,
|
||||
userId: userPart?.id,
|
||||
})
|
||||
const setProperty = (property, value) => setInput({
|
||||
...input,
|
||||
@@ -43,7 +44,7 @@ const PartProfile = ({
|
||||
const onImageUpload = ({cloudinaryPublicId}) => setProperty('mainImage', cloudinaryPublicId)
|
||||
const onEditSaveClick = () => {
|
||||
if (isEditable) {
|
||||
onSave(part.id, input)
|
||||
input.title && onSave(part?.id, input)
|
||||
return
|
||||
}
|
||||
navigate(routes.editPart2({userName: userPart.userName, partTitle: part.title}))
|
||||
@@ -98,7 +99,7 @@ const PartProfile = ({
|
||||
{/* main project center column */}
|
||||
<section className="col-start-3">
|
||||
<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"
|
||||
onImageUpload={onImageUpload}
|
||||
aspectRatio={16/9}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useAuth } from '@redwoodjs/auth'
|
||||
import { Flash } from '@redwoodjs/web'
|
||||
import Tooltip from '@material-ui/core/Tooltip';
|
||||
import { useQuery } from '@redwoodjs/web'
|
||||
import {getActiveClasses} from 'get-active-classes'
|
||||
|
||||
export const QUERY = gql`
|
||||
query FIND_USER_BY_ID($id: String!) {
|
||||
@@ -44,10 +45,10 @@ const MainLayout = ({ children }) => {
|
||||
</li>
|
||||
</ul>
|
||||
<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">
|
||||
{/* <Link to={routes.newPart()}> */}
|
||||
<Svg name="plus" className="text-indigo-300 w-full h-full" />
|
||||
{/* </Link> */}
|
||||
<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})}>
|
||||
<button className="h-full w-full" onClick={() => currentUser && routes.newPart2({userName: data?.user?.userName})}>
|
||||
<Svg name="plus" className={getActiveClasses("text-gray-700 w-full h-full",{'text-indigo-300': currentUser})} />
|
||||
</button>
|
||||
</li>
|
||||
{
|
||||
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