issue-159 Add delete button to part profile (for part owner)
schema had to be update to add a deleted boolean to part. Easier than setting up cascading deletes for comments and reactions. resolves #159
This commit is contained in:
@@ -0,0 +1,63 @@
|
|||||||
|
# Migration `20201213004819-add-delete-on-part`
|
||||||
|
|
||||||
|
This migration has been generated by Kurt Hutten at 12/13/2020, 11:48:20 AM.
|
||||||
|
You can check out the [state of the schema](./schema.prisma) after the migration.
|
||||||
|
|
||||||
|
## Database Steps
|
||||||
|
|
||||||
|
```sql
|
||||||
|
PRAGMA foreign_keys=OFF;
|
||||||
|
CREATE TABLE "new_Part" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"title" TEXT NOT NULL,
|
||||||
|
"description" TEXT,
|
||||||
|
"code" TEXT,
|
||||||
|
"mainImage" TEXT,
|
||||||
|
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" DATETIME NOT NULL,
|
||||||
|
"userId" TEXT NOT NULL,
|
||||||
|
"deleted" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
|
||||||
|
FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
INSERT INTO "new_Part" ("id", "title", "description", "code", "mainImage", "createdAt", "updatedAt", "userId") SELECT "id", "title", "description", "code", "mainImage", "createdAt", "updatedAt", "userId" FROM "Part";
|
||||||
|
DROP TABLE "Part";
|
||||||
|
ALTER TABLE "new_Part" RENAME TO "Part";
|
||||||
|
CREATE UNIQUE INDEX "Part.title_userId_unique" ON "Part"("title", "userId");
|
||||||
|
PRAGMA foreign_key_check;
|
||||||
|
PRAGMA foreign_keys=ON
|
||||||
|
```
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
```diff
|
||||||
|
diff --git schema.prisma schema.prisma
|
||||||
|
migration 20201105184423-add-name-to-user..20201213004819-add-delete-on-part
|
||||||
|
--- datamodel.dml
|
||||||
|
+++ datamodel.dml
|
||||||
|
@@ -1,12 +1,12 @@
|
||||||
|
datasource DS {
|
||||||
|
provider = ["sqlite", "postgresql"]
|
||||||
|
- url = "***"
|
||||||
|
+ url = "***"
|
||||||
|
}
|
||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
- binaryTargets = "native"
|
||||||
|
+ binaryTargets = ["native", "rhel-openssl-1.0.x"]
|
||||||
|
}
|
||||||
|
// sqlLight does not suport enums so we can't use enums until we set up postgresql in dev mode
|
||||||
|
// enum Role {
|
||||||
|
@@ -47,8 +47,9 @@
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
|
userId String
|
||||||
|
+ deleted Boolean @default(false)
|
||||||
|
Comment Comment[]
|
||||||
|
Reaction PartReaction[]
|
||||||
|
@@unique([title, userId])
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
datasource DS {
|
||||||
|
provider = ["sqlite", "postgresql"]
|
||||||
|
url = "***"
|
||||||
|
}
|
||||||
|
|
||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
binaryTargets = ["native", "rhel-openssl-1.0.x"]
|
||||||
|
}
|
||||||
|
|
||||||
|
// sqlLight does not suport enums so we can't use enums until we set up postgresql in dev mode
|
||||||
|
// enum Role {
|
||||||
|
// USER
|
||||||
|
// ADMIN
|
||||||
|
// }
|
||||||
|
|
||||||
|
// enum PartType {
|
||||||
|
// CASCADESTUDIO
|
||||||
|
// JSCAD
|
||||||
|
// }
|
||||||
|
|
||||||
|
model User {
|
||||||
|
id String @id @default(uuid())
|
||||||
|
userName String @unique // reffered to as userId in @relations
|
||||||
|
email String @unique
|
||||||
|
name String?
|
||||||
|
// role should probably be a list [] and also use enums, neither are supported by sqllight, so we need to set up postgresql in dev
|
||||||
|
// maybe let netlify handle roles for now.
|
||||||
|
// role String @default("user")
|
||||||
|
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
|
image String? // url maybe id or file storage service? cloudinary?
|
||||||
|
bio String? //mark down
|
||||||
|
Part Part[]
|
||||||
|
Reaction PartReaction[]
|
||||||
|
Comment Comment[]
|
||||||
|
}
|
||||||
|
|
||||||
|
model Part {
|
||||||
|
id String @id @default(uuid())
|
||||||
|
title String
|
||||||
|
description String? // markdown string
|
||||||
|
code String?
|
||||||
|
mainImage String? // link to cloudinary
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
|
userId String
|
||||||
|
deleted Boolean @default(false)
|
||||||
|
|
||||||
|
Comment Comment[]
|
||||||
|
Reaction PartReaction[]
|
||||||
|
@@unique([title, userId])
|
||||||
|
}
|
||||||
|
|
||||||
|
model PartReaction {
|
||||||
|
id String @id @default(uuid())
|
||||||
|
emote String // an emoji
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
|
userId String
|
||||||
|
part Part @relation(fields: [partId], references: [id])
|
||||||
|
partId String
|
||||||
|
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
@@unique([emote, userId, partId])
|
||||||
|
}
|
||||||
|
|
||||||
|
model Comment {
|
||||||
|
id String @id @default(uuid())
|
||||||
|
text String // the comment, should I allow mark down?
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
|
userId String
|
||||||
|
part Part @relation(fields: [partId], references: [id])
|
||||||
|
partId String
|
||||||
|
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"version": "0.3.14-fixed",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"tag": "CreateField",
|
||||||
|
"model": "Part",
|
||||||
|
"field": "deleted",
|
||||||
|
"type": "Boolean",
|
||||||
|
"arity": "Required"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tag": "CreateDirective",
|
||||||
|
"location": {
|
||||||
|
"path": {
|
||||||
|
"tag": "Field",
|
||||||
|
"model": "Part",
|
||||||
|
"field": "deleted"
|
||||||
|
},
|
||||||
|
"directive": "default"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tag": "CreateArgument",
|
||||||
|
"location": {
|
||||||
|
"tag": "Directive",
|
||||||
|
"path": {
|
||||||
|
"tag": "Field",
|
||||||
|
"model": "Part",
|
||||||
|
"field": "deleted"
|
||||||
|
},
|
||||||
|
"directive": "default"
|
||||||
|
},
|
||||||
|
"argument": "",
|
||||||
|
"value": "false"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
# Prisma Migrate lockfile v1
|
# Prisma Migrate lockfile v1
|
||||||
|
|
||||||
20201101183848-db-init
|
20201101183848-db-init
|
||||||
20201105184423-add-name-to-user
|
20201105184423-add-name-to-user
|
||||||
|
20201213004819-add-delete-on-part
|
||||||
@@ -48,6 +48,7 @@ model Part {
|
|||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
user User @relation(fields: [userId], references: [id])
|
user User @relation(fields: [userId], references: [id])
|
||||||
userId String
|
userId String
|
||||||
|
deleted Boolean @default(false)
|
||||||
|
|
||||||
Comment Comment[]
|
Comment Comment[]
|
||||||
Reaction PartReaction[]
|
Reaction PartReaction[]
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { requireAuth } from 'src/lib/auth'
|
|||||||
import { requireOwnership } from 'src/lib/owner'
|
import { requireOwnership } from 'src/lib/owner'
|
||||||
|
|
||||||
export const parts = () => {
|
export const parts = () => {
|
||||||
return db.part.findMany()
|
return db.part.findMany({ where: { deleted: false } })
|
||||||
}
|
}
|
||||||
|
|
||||||
export const part = ({ id }) => {
|
export const part = ({ id }) => {
|
||||||
@@ -70,9 +70,13 @@ export const updatePart = async ({ id, input }) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const deletePart = ({ id }) => {
|
export const deletePart = async ({ id }) => {
|
||||||
requireAuth()
|
requireAuth()
|
||||||
return db.part.delete({
|
await requireOwnership({ partId: id })
|
||||||
|
return db.part.update({
|
||||||
|
data: {
|
||||||
|
deleted: true,
|
||||||
|
},
|
||||||
where: { id },
|
where: { id },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,18 +8,23 @@ const Button = ({
|
|||||||
className,
|
className,
|
||||||
shouldAnimateHover,
|
shouldAnimateHover,
|
||||||
disabled,
|
disabled,
|
||||||
|
type,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
className={getActiveClasses(
|
className={getActiveClasses(
|
||||||
'flex items-center bg-opacity-50 rounded-xl p-2 px-6 text-indigo-600',
|
{
|
||||||
|
'bg-gray-300 shadow-none hover:shadow-none': disabled,
|
||||||
|
'text-red-600 bg-red-200 border border-red-600': type === 'danger',
|
||||||
|
'text-indigo-600': !type,
|
||||||
|
},
|
||||||
|
'flex items-center bg-opacity-50 rounded-xl p-2 px-6',
|
||||||
{
|
{
|
||||||
'mx-px transform hover:-translate-y-px transition-all duration-150':
|
'mx-px transform hover:-translate-y-px transition-all duration-150':
|
||||||
shouldAnimateHover && !disabled,
|
shouldAnimateHover && !disabled,
|
||||||
},
|
},
|
||||||
className,
|
className
|
||||||
{ 'bg-gray-300 shadow-none hover:shadow-none': disabled }
|
|
||||||
)}
|
)}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
|
|||||||
38
web/src/components/ConfirmDialog/ConfirmDialog.js
Normal file
38
web/src/components/ConfirmDialog/ConfirmDialog.js
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import Dialog from '@material-ui/core/Dialog'
|
||||||
|
import Button from 'src/components/Button'
|
||||||
|
|
||||||
|
const ConfirmDialog = ({ open, onClose, message, onConfirm }) => {
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onClose={onClose}>
|
||||||
|
<div className="bg-gray-100 max-w-3xl rounded-lg shadow-lg">
|
||||||
|
<div className="p-4">
|
||||||
|
<span className="text-gray-600 text-center">{message}</span>
|
||||||
|
<div className="flex gap-4">
|
||||||
|
<Button
|
||||||
|
className="mt-4 ml-auto shadow-md hover:shadow-lg bg-indigo-200 relative z-20"
|
||||||
|
shouldAnimateHover
|
||||||
|
iconName={'save'}
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
Don't delete
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className="mt-4 ml-auto shadow-md hover:shadow-lg bg-red-200 relative z-20"
|
||||||
|
shouldAnimateHover
|
||||||
|
iconName={'trash'}
|
||||||
|
onClick={() => {
|
||||||
|
onClose()
|
||||||
|
onConfirm()
|
||||||
|
}}
|
||||||
|
type="danger"
|
||||||
|
>
|
||||||
|
Yes, Delete
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Dialog>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ConfirmDialog
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import ConfirmDialog from './ConfirmDialog'
|
||||||
|
|
||||||
|
export const generated = () => {
|
||||||
|
return <ConfirmDialog />
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { title: 'Components/ConfirmDialog' }
|
||||||
11
web/src/components/ConfirmDialog/ConfirmDialog.test.js
Normal file
11
web/src/components/ConfirmDialog/ConfirmDialog.test.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { render } from '@redwoodjs/testing'
|
||||||
|
|
||||||
|
import ConfirmDialog from './ConfirmDialog'
|
||||||
|
|
||||||
|
describe('ConfirmDialog', () => {
|
||||||
|
it('renders successfully', () => {
|
||||||
|
expect(() => {
|
||||||
|
render(<ConfirmDialog />)
|
||||||
|
}).not.toThrow()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -84,6 +84,18 @@ const CREATE_COMMENT_MUTATION = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
const DELETE_PART_MUTATION = gql`
|
||||||
|
mutation DeletePartMutation($id: String!) {
|
||||||
|
deletePart(id: $id) {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
user {
|
||||||
|
id
|
||||||
|
userName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
export const Loading = () => <div>Loading...</div>
|
export const Loading = () => <div>Loading...</div>
|
||||||
|
|
||||||
@@ -123,6 +135,16 @@ export const Success = ({ userPart, variables: { isEditable }, refetch }) => {
|
|||||||
}
|
}
|
||||||
updateUser({ variables: { id, input } })
|
updateUser({ variables: { id, input } })
|
||||||
}
|
}
|
||||||
|
const [deletePart] = useMutation(DELETE_PART_MUTATION, {
|
||||||
|
onCompleted: ({ deletePart }) => {
|
||||||
|
navigate(routes.home())
|
||||||
|
addMessage('Part deleted.', { classes: 'rw-flash-success' })
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const onDelete = () => {
|
||||||
|
userPart?.Part?.id && deletePart({ variables: { id: userPart?.Part?.id } })
|
||||||
|
}
|
||||||
|
|
||||||
const [toggleReaction] = useMutation(TOGGLE_REACTION_MUTATION, {
|
const [toggleReaction] = useMutation(TOGGLE_REACTION_MUTATION, {
|
||||||
onCompleted: () => refetch(),
|
onCompleted: () => refetch(),
|
||||||
@@ -156,6 +178,7 @@ export const Success = ({ userPart, variables: { isEditable }, refetch }) => {
|
|||||||
<PartProfile
|
<PartProfile
|
||||||
userPart={userPart}
|
userPart={userPart}
|
||||||
onSave={onSave}
|
onSave={onSave}
|
||||||
|
onDelete={onDelete}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
error={error}
|
error={error}
|
||||||
isEditable={isEditable}
|
isEditable={isEditable}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { Link, navigate, routes } from '@redwoodjs/router'
|
|||||||
import Editor from 'rich-markdown-editor'
|
import Editor from 'rich-markdown-editor'
|
||||||
|
|
||||||
import ImageUploader from 'src/components/ImageUploader'
|
import ImageUploader from 'src/components/ImageUploader'
|
||||||
|
import ConfirmDialog from 'src/components/ConfirmDialog'
|
||||||
import Breadcrumb from 'src/components/Breadcrumb'
|
import Breadcrumb from 'src/components/Breadcrumb'
|
||||||
import EmojiReaction from 'src/components/EmojiReaction'
|
import EmojiReaction from 'src/components/EmojiReaction'
|
||||||
import Button from 'src/components/Button'
|
import Button from 'src/components/Button'
|
||||||
@@ -14,14 +15,15 @@ const PartProfile = ({
|
|||||||
userPart,
|
userPart,
|
||||||
isEditable,
|
isEditable,
|
||||||
onSave,
|
onSave,
|
||||||
|
onDelete,
|
||||||
onReaction,
|
onReaction,
|
||||||
onComment,
|
onComment,
|
||||||
}) => {
|
}) => {
|
||||||
const [comment, setComment] = useState('')
|
const [comment, setComment] = useState('')
|
||||||
|
const [isConfirmDialogOpen, setIsConfirmDialogOpen] = useState(false)
|
||||||
const { currentUser } = useAuth()
|
const { currentUser } = useAuth()
|
||||||
const canEdit = currentUser?.sub === userPart.id
|
const canEdit = currentUser?.sub === userPart.id
|
||||||
const part = userPart?.Part
|
const part = userPart?.Part
|
||||||
console.log(part)
|
|
||||||
const emotes = countEmotes(part?.Reaction)
|
const emotes = countEmotes(part?.Reaction)
|
||||||
const userEmotes = part?.userReactions.map(({ emote }) => emote)
|
const userEmotes = part?.userReactions.map(({ emote }) => emote)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -100,7 +102,7 @@ const PartProfile = ({
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Button
|
<Button
|
||||||
className="mt-4 ml-auto shadow-md hover:shadow-lg bg-indigo-200"
|
className="mt-4 ml-auto shadow-md hover:shadow-lg bg-indigo-200 w-full justify-end"
|
||||||
shouldAnimateHover
|
shouldAnimateHover
|
||||||
iconName="terminal"
|
iconName="terminal"
|
||||||
onClick={() => {}}
|
onClick={() => {}}
|
||||||
@@ -109,15 +111,27 @@ const PartProfile = ({
|
|||||||
</Button>
|
</Button>
|
||||||
</Link>
|
</Link>
|
||||||
{canEdit && (
|
{canEdit && (
|
||||||
<Button
|
<>
|
||||||
className="mt-4 ml-auto shadow-md hover:shadow-lg bg-indigo-200 relative z-20"
|
<Button
|
||||||
shouldAnimateHover
|
className="mt-4 ml-auto shadow-md hover:shadow-lg bg-indigo-200 relative z-20 w-full justify-end"
|
||||||
iconName={isEditable ? 'save' : 'pencil'}
|
shouldAnimateHover
|
||||||
onClick={onEditSaveClick}
|
iconName={isEditable ? 'save' : 'pencil'}
|
||||||
>
|
onClick={onEditSaveClick}
|
||||||
{isEditable ? 'Save Details' : 'Edit Details'}
|
>
|
||||||
</Button>
|
{isEditable ? 'Save Details' : 'Edit Details'}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
className="mt-4 ml-auto shadow-md hover:shadow-lg bg-red-200 relative z-20 w-full justify-end"
|
||||||
|
shouldAnimateHover
|
||||||
|
iconName={'trash'}
|
||||||
|
onClick={() => setIsConfirmDialogOpen(true)}
|
||||||
|
type="danger"
|
||||||
|
>
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
|
{/* gray overlay */}
|
||||||
{isEditable && (
|
{isEditable && (
|
||||||
<div className="absolute inset-0 bg-gray-300 opacity-75 z-10 transform scale-x-110 -ml-1 -mt-2" />
|
<div className="absolute inset-0 bg-gray-300 opacity-75 z-10 transform scale-x-110 -ml-1 -mt-2" />
|
||||||
)}
|
)}
|
||||||
@@ -216,6 +230,12 @@ const PartProfile = ({
|
|||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
<ConfirmDialog
|
||||||
|
open={isConfirmDialogOpen}
|
||||||
|
onClose={() => setIsConfirmDialogOpen(false)}
|
||||||
|
onConfirm={onDelete}
|
||||||
|
message="Are you sure you want to delete? This action cannot be undone."
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -307,6 +307,21 @@ const Svg = ({ name, className: className2, strokeWidth = 2 }) => {
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
),
|
),
|
||||||
|
trash: (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={strokeWidth}
|
||||||
|
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div className={className2 || 'h-10 w-10'}>{svgs[name]}</div>
|
return <div className={className2 || 'h-10 w-10'}>{svgs[name]}</div>
|
||||||
|
|||||||
@@ -91,7 +91,6 @@ const MainLayout = ({ children }) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const [key, token] = hash.slice(1).split('=')
|
const [key, token] = hash.slice(1).split('=')
|
||||||
if (key === 'confirmation_token') {
|
if (key === 'confirmation_token') {
|
||||||
console.log('confirming with', token)
|
|
||||||
client
|
client
|
||||||
.confirm(token, true)
|
.confirm(token, true)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@@ -209,7 +208,7 @@ const MainLayout = ({ children }) => {
|
|||||||
horizontal: 'right',
|
horizontal: 'right',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="p-4 w-40">
|
<div className="p-4 w-48">
|
||||||
<Link to={routes.user({ userName: data?.user?.userName })}>
|
<Link to={routes.user({ userName: data?.user?.userName })}>
|
||||||
<h3 className="text-indigo-800" style={{ fontWeight: '500' }}>
|
<h3 className="text-indigo-800" style={{ fontWeight: '500' }}>
|
||||||
Hello {data?.user?.name}
|
Hello {data?.user?.name}
|
||||||
@@ -217,8 +216,8 @@ const MainLayout = ({ children }) => {
|
|||||||
</Link>
|
</Link>
|
||||||
<hr />
|
<hr />
|
||||||
<br />
|
<br />
|
||||||
<Link to={routes.editUser({ userName: data?.user?.userName })}>
|
<Link to={routes.user({ userName: data?.user?.userName })}>
|
||||||
<div className="text-indigo-800">Edit Profile</div>
|
<div className="text-indigo-800">Your Profile</div>
|
||||||
</Link>
|
</Link>
|
||||||
<a href="#" className="text-indigo-800" onClick={logOut}>
|
<a href="#" className="text-indigo-800" onClick={logOut}>
|
||||||
Logout
|
Logout
|
||||||
|
|||||||
Reference in New Issue
Block a user