feat(ui): hides lock icon when locked by current user (#8309)
This commit is contained in:
@@ -140,7 +140,7 @@ export const DefaultDashboard: React.FC<DashboardProps> = (props) => {
|
||||
<li key={entityIndex}>
|
||||
<Card
|
||||
actions={
|
||||
lockStatus ? (
|
||||
lockStatus && user?.id !== userEditing?.id ? (
|
||||
<Locked className={`${baseClass}__locked`} user={userEditing} />
|
||||
) : hasCreatePermission && type === EntityType.collection ? (
|
||||
<Button
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
StaggeredShimmers,
|
||||
Table,
|
||||
UnpublishMany,
|
||||
useAuth,
|
||||
useBulkUpload,
|
||||
useConfig,
|
||||
useEditDepth,
|
||||
@@ -43,6 +44,7 @@ const baseClass = 'collection-list'
|
||||
const Link = (LinkImport.default || LinkImport) as unknown as typeof LinkImport.default
|
||||
|
||||
export const DefaultListView: React.FC = () => {
|
||||
const { user } = useAuth()
|
||||
const {
|
||||
beforeActions,
|
||||
collectionSlug,
|
||||
@@ -125,7 +127,7 @@ export const DefaultListView: React.FC = () => {
|
||||
return (
|
||||
<div className={`${baseClass} ${baseClass}--${collectionSlug}`}>
|
||||
<SetViewActions actions={actions} />
|
||||
<SelectionProvider docs={data.docs} totalDocs={data.totalDocs}>
|
||||
<SelectionProvider docs={data.docs} totalDocs={data.totalDocs} user={user}>
|
||||
<RenderComponent mappedComponent={beforeList} />
|
||||
<Gutter className={`${baseClass}__wrap`}>
|
||||
{Header || (
|
||||
|
||||
@@ -3,6 +3,7 @@ import React from 'react'
|
||||
|
||||
import { useTableCell } from '../../elements/Table/TableCellProvider/index.js'
|
||||
import { CheckboxInput } from '../../fields/Checkbox/Input.js'
|
||||
import { useAuth } from '../../providers/Auth/index.js'
|
||||
import { useSelection } from '../../providers/Selection/index.js'
|
||||
import { Locked } from '../Locked/index.js'
|
||||
import './index.scss'
|
||||
@@ -10,13 +11,14 @@ import './index.scss'
|
||||
const baseClass = 'select-row'
|
||||
|
||||
export const SelectRow: React.FC = () => {
|
||||
const { user } = useAuth()
|
||||
const { selected, setSelection } = useSelection()
|
||||
const { rowData } = useTableCell()
|
||||
const { _isLocked, _userEditing } = rowData || {}
|
||||
|
||||
const documentIsLocked = _isLocked && _userEditing
|
||||
|
||||
if (documentIsLocked) {
|
||||
if (documentIsLocked && _userEditing.id !== user?.id) {
|
||||
return <Locked user={_userEditing} />
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
'use client'
|
||||
import type { Where } from 'payload'
|
||||
import type { ClientUser, Where } from 'payload'
|
||||
|
||||
import * as qs from 'qs-esm'
|
||||
import React, { createContext, useCallback, useContext, useEffect, useRef, useState } from 'react'
|
||||
@@ -33,9 +33,10 @@ type Props = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
readonly docs: any[]
|
||||
readonly totalDocs: number
|
||||
user: ClientUser
|
||||
}
|
||||
|
||||
export const SelectionProvider: React.FC<Props> = ({ children, docs = [], totalDocs }) => {
|
||||
export const SelectionProvider: React.FC<Props> = ({ children, docs = [], totalDocs, user }) => {
|
||||
const contextRef = useRef({} as SelectionContext)
|
||||
|
||||
const { code: locale } = useLocale()
|
||||
@@ -56,8 +57,8 @@ export const SelectionProvider: React.FC<Props> = ({ children, docs = [], totalD
|
||||
const rows = new Map()
|
||||
if (allAvailable) {
|
||||
setSelectAll(SelectAllStatus.AllAvailable)
|
||||
docs.forEach(({ id, _isLocked }) => {
|
||||
if (!_isLocked) {
|
||||
docs.forEach(({ id, _isLocked, _userEditing }) => {
|
||||
if (!_isLocked || _userEditing?.id === user?.id) {
|
||||
rows.set(id, true)
|
||||
}
|
||||
})
|
||||
@@ -67,22 +68,22 @@ export const SelectionProvider: React.FC<Props> = ({ children, docs = [], totalD
|
||||
) {
|
||||
setSelectAll(SelectAllStatus.None)
|
||||
} else {
|
||||
docs.forEach(({ id, _isLocked }) => {
|
||||
if (!_isLocked) {
|
||||
docs.forEach(({ id, _isLocked, _userEditing }) => {
|
||||
if (!_isLocked || _userEditing?.id === user?.id) {
|
||||
rows.set(id, selectAll !== SelectAllStatus.Some)
|
||||
}
|
||||
})
|
||||
}
|
||||
setSelected(rows)
|
||||
},
|
||||
[docs, selectAll],
|
||||
[docs, selectAll, user?.id],
|
||||
)
|
||||
|
||||
const setSelection = useCallback(
|
||||
(id) => {
|
||||
const doc = docs.find((doc) => doc.id === id)
|
||||
|
||||
if (doc?._isLocked) {
|
||||
if (doc?._isLocked && user?.id !== doc?._userEditing.id) {
|
||||
return // Prevent selection if the document is locked
|
||||
}
|
||||
|
||||
@@ -99,7 +100,7 @@ export const SelectionProvider: React.FC<Props> = ({ children, docs = [], totalD
|
||||
|
||||
setSelected(newMap)
|
||||
},
|
||||
[selected, docs],
|
||||
[selected, docs, user?.id],
|
||||
)
|
||||
|
||||
const getQueryParams = useCallback(
|
||||
|
||||
@@ -70,6 +70,7 @@ describe('locked documents', () => {
|
||||
|
||||
describe('list view - collections', () => {
|
||||
let postDoc
|
||||
let anotherPostDoc
|
||||
let user2
|
||||
let lockedDoc
|
||||
|
||||
@@ -78,6 +79,10 @@ describe('locked documents', () => {
|
||||
text: 'hello',
|
||||
})
|
||||
|
||||
anotherPostDoc = await createPostDoc({
|
||||
text: 'another post',
|
||||
})
|
||||
|
||||
user2 = await payload.create({
|
||||
collection: 'users',
|
||||
data: {
|
||||
@@ -114,6 +119,11 @@ describe('locked documents', () => {
|
||||
id: postDoc.id,
|
||||
})
|
||||
|
||||
await payload.delete({
|
||||
collection: 'posts',
|
||||
id: anotherPostDoc.id,
|
||||
})
|
||||
|
||||
await payload.delete({
|
||||
collection: 'users',
|
||||
id: user2.id,
|
||||
@@ -124,14 +134,29 @@ describe('locked documents', () => {
|
||||
await page.goto(postsUrl.list)
|
||||
await page.waitForURL(postsUrl.list)
|
||||
|
||||
await expect(page.locator('.table .row-1 .locked svg')).toBeVisible()
|
||||
await expect(page.locator('.table .row-2 .locked svg')).toBeVisible()
|
||||
})
|
||||
|
||||
test('should show no lock icon on document row if unlocked', async () => {
|
||||
test('should not show lock icon on document row if unlocked', async () => {
|
||||
await page.goto(postsUrl.list)
|
||||
await page.waitForURL(postsUrl.list)
|
||||
|
||||
await expect(page.locator('.table .row-2 .checkbox-input__input')).toBeVisible()
|
||||
await expect(page.locator('.table .row-3 .checkbox-input__input')).toBeVisible()
|
||||
})
|
||||
|
||||
test('should not show lock icon on document row if locked by current user', async () => {
|
||||
await page.goto(postsUrl.edit(anotherPostDoc.id))
|
||||
await page.waitForURL(postsUrl.edit(anotherPostDoc.id))
|
||||
|
||||
const textInput = page.locator('#field-text')
|
||||
await textInput.fill('testing')
|
||||
|
||||
await page.reload()
|
||||
|
||||
await page.goto(postsUrl.list)
|
||||
await page.waitForURL(postsUrl.list)
|
||||
|
||||
await expect(page.locator('.table .row-1 .checkbox-input__input')).toBeVisible()
|
||||
})
|
||||
|
||||
test('should only allow bulk delete on unlocked documents', async () => {
|
||||
@@ -139,7 +164,7 @@ describe('locked documents', () => {
|
||||
await page.locator('input#select-all').check()
|
||||
await page.locator('.delete-documents__toggle').click()
|
||||
await expect(page.locator('.delete-documents__content p')).toHaveText(
|
||||
'You are about to delete 1 Posts',
|
||||
'You are about to delete 2 Posts',
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -744,6 +769,7 @@ describe('locked documents', () => {
|
||||
|
||||
describe('dashboard - globals', () => {
|
||||
let user2
|
||||
let lockedGlobal
|
||||
|
||||
beforeAll(async () => {
|
||||
user2 = await payload.create({
|
||||
@@ -753,6 +779,19 @@ describe('locked documents', () => {
|
||||
password: '1234',
|
||||
},
|
||||
})
|
||||
|
||||
lockedGlobal = await payload.create({
|
||||
collection: lockedDocumentCollection,
|
||||
data: {
|
||||
document: undefined,
|
||||
editedAt: new Date().toISOString(),
|
||||
globalSlug: 'menu',
|
||||
user: {
|
||||
relationTo: 'users',
|
||||
value: user2.id,
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
@@ -763,17 +802,17 @@ describe('locked documents', () => {
|
||||
})
|
||||
|
||||
test('should show lock on document card in dashboard view if locked', async () => {
|
||||
const lockedGlobal = await payload.create({
|
||||
await page.goto(postsUrl.admin)
|
||||
await page.waitForURL(postsUrl.admin)
|
||||
|
||||
const globalCardList = page.locator('.dashboard__group').nth(1)
|
||||
await expect(globalCardList.locator('#card-menu .locked svg')).toBeVisible()
|
||||
})
|
||||
|
||||
test('should not show lock on document card in dashboard view if unlocked', async () => {
|
||||
await payload.delete({
|
||||
collection: lockedDocumentCollection,
|
||||
data: {
|
||||
document: undefined,
|
||||
editedAt: new Date().toISOString(),
|
||||
globalSlug: 'menu',
|
||||
user: {
|
||||
relationTo: 'users',
|
||||
value: user2.id,
|
||||
},
|
||||
},
|
||||
id: lockedGlobal.id,
|
||||
})
|
||||
|
||||
// eslint-disable-next-line payload/no-wait-function
|
||||
@@ -783,15 +822,18 @@ describe('locked documents', () => {
|
||||
await page.waitForURL(postsUrl.admin)
|
||||
|
||||
const globalCardList = page.locator('.dashboard__group').nth(1)
|
||||
await expect(globalCardList.locator('#card-menu .locked svg')).toBeVisible()
|
||||
|
||||
await payload.delete({
|
||||
collection: lockedDocumentCollection,
|
||||
id: lockedGlobal.id,
|
||||
})
|
||||
await expect(globalCardList.locator('#card-menu .locked')).toBeHidden()
|
||||
})
|
||||
|
||||
test('should show no lock on document card in dashboard view if unlocked', async () => {
|
||||
test('should not show lock on document card in dashboard view if locked by current user', async () => {
|
||||
await page.goto(postsUrl.edit('menu'))
|
||||
await page.waitForURL(postsUrl.edit('menu'))
|
||||
|
||||
const textInput = page.locator('#field-text')
|
||||
await textInput.fill('this is a global menu text field')
|
||||
|
||||
await page.reload()
|
||||
|
||||
await page.goto(postsUrl.admin)
|
||||
await page.waitForURL(postsUrl.admin)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user