feat: tags (#11980)

* feat: tags

* fix: folder tree icons

* navigate to tag from detail panel

* delete tag

* Tag position and add tag button

* Tag asset in detail panel

* refactor form

* feat: navigate to tag page from clicking on a tag

* feat: delete tags from the tag page

* refactor: moving tag section in detail panel and add + tag button

* feat: tag asset action in detail panel

* refactor add tag form

* fdisable add tag button when there is no selection

* feat: tag bulk endpoint

* feat: tag colors

* chore: clean up

* chore: unit tests

* feat: write tags to sidecar

* Remove tag and auto focus on tag creation form opened

* chore: regenerate migration

* chore: linting

* add color picker to tag edit form

* fix: force render tags timeline on navigating back from asset viewer

* feat: read tags from keywords

* chore: clean up

---------

Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
Jason Rasmussen
2024-08-29 12:14:03 -04:00
committed by GitHub
parent 682adaa334
commit d08a20bd57
68 changed files with 3032 additions and 814 deletions

View File

@@ -1,24 +1,65 @@
import { TagResponseDto } from 'src/dtos/tag.dto';
import { TagEntity, TagType } from 'src/entities/tag.entity';
import { TagEntity } from 'src/entities/tag.entity';
import { userStub } from 'test/fixtures/user.stub';
const parent = Object.freeze<TagEntity>({
id: 'tag-parent',
createdAt: new Date('2021-01-01T00:00:00Z'),
updatedAt: new Date('2021-01-01T00:00:00Z'),
value: 'Parent',
color: null,
userId: userStub.admin.id,
user: userStub.admin,
});
const child = Object.freeze<TagEntity>({
id: 'tag-child',
createdAt: new Date('2021-01-01T00:00:00Z'),
updatedAt: new Date('2021-01-01T00:00:00Z'),
value: 'Parent/Child',
color: null,
parent,
userId: userStub.admin.id,
user: userStub.admin,
});
export const tagStub = {
tag1: Object.freeze<TagEntity>({
id: 'tag-1',
name: 'Tag1',
type: TagType.CUSTOM,
createdAt: new Date('2021-01-01T00:00:00Z'),
updatedAt: new Date('2021-01-01T00:00:00Z'),
value: 'Tag1',
color: null,
userId: userStub.admin.id,
user: userStub.admin,
}),
parent,
child,
color1: Object.freeze<TagEntity>({
id: 'tag-1',
createdAt: new Date('2021-01-01T00:00:00Z'),
updatedAt: new Date('2021-01-01T00:00:00Z'),
value: 'Tag1',
color: '#000000',
userId: userStub.admin.id,
user: userStub.admin,
renameTagId: null,
assets: [],
}),
};
export const tagResponseStub = {
tag1: Object.freeze<TagResponseDto>({
id: 'tag-1',
createdAt: new Date('2021-01-01T00:00:00Z'),
updatedAt: new Date('2021-01-01T00:00:00Z'),
name: 'Tag1',
type: 'CUSTOM',
userId: 'admin_id',
value: 'Tag1',
}),
color1: Object.freeze<TagResponseDto>({
id: 'tag-1',
createdAt: new Date('2021-01-01T00:00:00Z'),
updatedAt: new Date('2021-01-01T00:00:00Z'),
color: '#000000',
name: 'Tag1',
value: 'Tag1',
}),
};

View File

@@ -11,6 +11,7 @@ export interface IAccessRepositoryMock {
partner: Mocked<IAccessRepository['partner']>;
stack: Mocked<IAccessRepository['stack']>;
timeline: Mocked<IAccessRepository['timeline']>;
tag: Mocked<IAccessRepository['tag']>;
}
export const newAccessRepositoryMock = (): IAccessRepositoryMock => {
@@ -58,5 +59,9 @@ export const newAccessRepositoryMock = (): IAccessRepositoryMock => {
timeline: {
checkPartnerAccess: vitest.fn().mockResolvedValue(new Set()),
},
tag: {
checkOwnerAccess: vitest.fn().mockResolvedValue(new Set()),
},
};
};

View File

@@ -4,14 +4,17 @@ import { Mocked, vitest } from 'vitest';
export const newTagRepositoryMock = (): Mocked<ITagRepository> => {
return {
getAll: vitest.fn(),
getById: vitest.fn(),
getByValue: vitest.fn(),
upsertAssetTags: vitest.fn(),
get: vitest.fn(),
create: vitest.fn(),
update: vitest.fn(),
remove: vitest.fn(),
hasAsset: vitest.fn(),
hasName: vitest.fn(),
getAssets: vitest.fn(),
addAssets: vitest.fn(),
removeAssets: vitest.fn(),
delete: vitest.fn(),
getAssetIds: vitest.fn(),
addAssetIds: vitest.fn(),
removeAssetIds: vitest.fn(),
upsertAssetIds: vitest.fn(),
};
};