Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature 19: Favoriten | Erstellung & Verwaltung #322

Merged
merged 1 commit into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,23 @@
asset-sg-menu-bar-item
icon="assets"
[link]="isActive ? null : [translateService.currentLang]"
[isActive]="isActive && ((isFiltersOpen$ | async) ?? false)"
[isActive]="isActive"
(click)="isActive ? toggleAssetDrawer() : null"
>
menuBar.filters
</li>
<li *ngIf="userExists$ | async" asset-sg-menu-bar-item icon="favourite" disabled>menuBar.favourites</li>
@if (userExists$ | async) {
<li
*rxLet="activeItem === 'favorites'; let isActive"
asset-sg-menu-bar-item
icon="favorite"
[link]="isActive ? null : [translateService.currentLang, 'favorites']"
[isActive]="isActive"
(click)="isActive ? toggleAssetDrawer() : null"
>
menuBar.favourites
</li>
}
<li
*canCreate="AssetEditPolicy"
asset-sg-menu-bar-item
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ export class MenuBarComponent {

readonly activeItem$: Observable<MenuItem | null> = this.router.events.pipe(
filter((event) => event instanceof NavigationEnd),
startWith(() => undefined),
map((): MenuItem | null => {
const segments = this.router.parseUrl(this.router.url).root.children['primary'].segments;
if (segments.length === 1) {
const segments = (this.router.getCurrentNavigation() ?? this.router.lastSuccessfulNavigation)?.finalUrl?.root
.children['primary'].segments;
if (segments == null || segments.length === 1) {
return 'home';
}
const path = segments.slice(1).join('/');
Expand All @@ -43,6 +45,9 @@ export class MenuBarComponent {
if (isPath('asset-admin/new')) {
return 'create-asset';
}
if (isPath('favorites')) {
return 'favorites';
}
if (path == 'asset-admin' || isPath('admin')) {
return 'options';
}
Expand All @@ -58,4 +63,4 @@ export class MenuBarComponent {
protected readonly AssetEditPolicy = AssetEditPolicy;
}

type MenuItem = 'home' | 'create-asset' | 'options';
type MenuItem = 'home' | 'favorites' | 'create-asset' | 'options';
3 changes: 3 additions & 0 deletions apps/client-asset-sg/src/app/i18n/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ export const deAppTranslations = {
nameTaken: "Der Name '{{name}}' wird bereits von einer anderen Arbeitsgruppe verwendet.",
},
},
favorites: {
title: 'Favoriten',
},
menuBar: {
filters: 'Filter',
admin: 'Verwaltung',
Expand Down
3 changes: 3 additions & 0 deletions apps/client-asset-sg/src/app/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ export const enAppTranslations: AppTranslations = {
nameTaken: "The name '{{name}}' is already taken by another workgroup.",
},
},
favorites: {
title: 'Favorites',
},
menuBar: {
filters: 'Filters',
admin: 'Administration',
Expand Down
3 changes: 3 additions & 0 deletions apps/client-asset-sg/src/app/i18n/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ export const frAppTranslations: AppTranslations = {
nameTaken: "Le nom '{{name}}' est déjà utilisé par un autre groupe de travail.",
},
},
favorites: {
title: 'Favorites',
},
menuBar: {
filters: 'Filtres',
admin: 'Administration',
Expand Down
3 changes: 3 additions & 0 deletions apps/client-asset-sg/src/app/i18n/it.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ export const itAppTranslations: AppTranslations = {
nameTaken: "IT Der Name '{{name}}' wird bereits von einer anderen Arbeitsgruppe verwendet.",
},
},
favorites: {
title: 'IT Favoriten',
},
menuBar: {
filters: 'IT Filter',
admin: 'IT Verwaltung',
Expand Down
3 changes: 3 additions & 0 deletions apps/client-asset-sg/src/app/i18n/rm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ export const rmAppTranslations: AppTranslations = {
nameTaken: "RM Name '{{name}}' wird bereits von einer anderen Arbeitsgruppe verwendet.",
},
},
favorites: {
title: 'RM Favoriten',
},
menuBar: {
filters: 'RM Filter',
admin: 'RM Verwaltung',
Expand Down
24 changes: 20 additions & 4 deletions apps/server-asset-sg/src/app.logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,30 @@ export class AppLogger implements LoggerService {
if (!(message instanceof Error)) {
output += level.color(`${message}`);
}
const suffix = [];
if (params.length !== 0 && !(params.length === 1 && params[0] === undefined)) {
output += ' ' + stringify(params, level);
let i = 0;
while (typeof params[i] === 'string') {
const line = params[i] as string;
suffix.push(line);
i += 1;
if (i >= params.length || line.endsWith('\n')) {
break;
}
}
params = params.slice(i);
if (params.length !== 0) {
output += ' ' + stringify(params, level);
}
}
const args: unknown[] = [`${prefix} ${output}`];
if (message instanceof Error) {
console.log(`${prefix} ${output}`, message);
} else {
console.log(`${prefix} ${output}`);
args.push(message);
}
if (suffix.length !== 0) {
args.push(level.color(`\n${suffix.join('\n')}`));
}
console.log(...args);
}
}

Expand Down
2 changes: 1 addition & 1 deletion apps/server-asset-sg/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ import { WorkgroupsController } from '@/features/workgroups/workgroups.controlle
@Module({
controllers: [
AppController,
FavoritesController,
AssetEditController,
AssetSearchController,
AssetSyncController,
AssetsController,
ContactsController,
FavoritesController,
FilesController,
StudiesController,
UsersController,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { fakeAssetFormatItemCode } from '../../../../../test/data/asset-format-i
// eslint-disable-next-line @nx/enforce-module-boundaries
import { fakeAssetKindItemCode } from '../../../../../test/data/asset-kind-item';
// eslint-disable-next-line @nx/enforce-module-boundaries
import { fakeContactKindItem } from '../../../../../test/data/contact-kind-item';
import { fakeContactKindItemCode } from '../../../../../test/data/contact-kind-item';

import { define } from '@/utils/define';

Expand All @@ -35,7 +35,7 @@ export const fakeUser = () => {

export const fakeContact = () =>
define<Omit<Contact, 'id'>>({
contactKindItemCode: fakeContactKindItem(),
contactKindItemCode: fakeContactKindItemCode(),
name: faker.company.name(),
street: faker.location.street(),
houseNumber: faker.location.buildingNumber(),
Expand Down
18 changes: 17 additions & 1 deletion apps/server-asset-sg/src/features/assets/asset-info.repo.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { AssetId, AssetInfo } from '@asset-sg/shared/v2';
import { AssetId, AssetInfo, UserId } from '@asset-sg/shared/v2';
import { Injectable } from '@nestjs/common';
import { PrismaService } from '@/core/prisma.service';
import { ReadRepo, RepoListOptions } from '@/core/repo';
import { assetInfoSelection, parseAssetInfoFromPrisma } from '@/features/assets/prisma-asset';

@Injectable()
export class AssetInfoRepo implements ReadRepo<AssetInfo, AssetId> {
constructor(private readonly prisma: PrismaService) {}

Expand All @@ -28,4 +30,18 @@ export class AssetInfoRepo implements ReadRepo<AssetInfo, AssetId> {
});
return entries.map(parseAssetInfoFromPrisma);
}

async listFavorites(userId: UserId): Promise<AssetInfo[]> {
const entries = await this.prisma.asset.findMany({
where: {
favorites: {
some: {
userId: userId,
},
},
},
select: assetInfoSelection,
});
return entries.map(parseAssetInfoFromPrisma);
}
}
4 changes: 2 additions & 2 deletions apps/server-asset-sg/src/features/assets/asset.repo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import { handlePrismaMutationError } from '@/utils/prisma';
export class AssetRepo implements FindRepo<Asset, AssetId>, MutateRepo<Asset, AssetId, FullAssetData> {
constructor(private readonly prisma: PrismaService) {}

async count() {
return await this.prisma.asset.count();
async count(): Promise<number> {
return this.prisma.asset.count();
}

async find(id: AssetId): Promise<Asset | null> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export class AssetSearchController {
limit = limit == null ? limit : Number(limit);
offset = offset == null ? offset : Number(offset);
restrictQueryForUser(query, user);
const result = await this.assetSearchService.search(query, { limit, offset, decode: false });
const result = await this.assetSearchService.search(query, user, { limit, offset, decode: false });
return plainToInstance(AssetSearchResultDTO, result);
}

Expand All @@ -48,7 +48,7 @@ export class AssetSearchController {
@CurrentUser() user: User
): Promise<AssetSearchStats> {
restrictQueryForUser(query, user);
const stats = await this.assetSearchService.aggregate(query);
const stats = await this.assetSearchService.aggregate(query, user);
return plainToInstance(AssetSearchStatsDTO, stats);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ describe(AssetSearchService, () => {
await create({ patch: fakeAssetPatch(), user });

// When
const result = await service.search({ text: `${text}` });
const result = await service.search({ text: `${text}` }, user);

// Then
assertSingleResult(result, asset);
Expand Down Expand Up @@ -194,11 +194,14 @@ describe(AssetSearchService, () => {
});

// When
const result = await service.search({
createDate: {
min: new Date(dateFromDateId(asset.createDate).getTime() - millisPerDay),
const result = await service.search(
{
createDate: {
min: new Date(dateFromDateId(asset.createDate).getTime() - millisPerDay),
},
},
});
user
);

// Then
assertSingleResult(result, asset);
Expand All @@ -217,11 +220,14 @@ describe(AssetSearchService, () => {
});

// When
const result = await service.search({
createDate: {
max: new Date(dateFromDateId(asset.createDate).getTime() + millisPerDay),
const result = await service.search(
{
createDate: {
max: new Date(dateFromDateId(asset.createDate).getTime() + millisPerDay),
},
},
});
user
);

// Then
assertSingleResult(result, asset);
Expand All @@ -244,14 +250,18 @@ describe(AssetSearchService, () => {
},
user: fakeUser(),
});
const user = fakeUser();

// When
const result = await service.search({
createDate: {
min: new Date(dateFromDateId(asset.createDate).getTime() - millisPerDay),
max: new Date(dateFromDateId(asset.createDate).getTime() + millisPerDay),
const result = await service.search(
{
createDate: {
min: new Date(dateFromDateId(asset.createDate).getTime() - millisPerDay),
max: new Date(dateFromDateId(asset.createDate).getTime() + millisPerDay),
},
},
});
user
);

// Then
assertSingleResult(result, asset);
Expand All @@ -274,9 +284,10 @@ describe(AssetSearchService, () => {
patch: { ...fakeAssetPatch(), assetLanguages: [{ languageItemCode: code3 }] },
user: fakeUser(),
});
const user = fakeUser();

// When
const result = await service.search({ languageItemCodes: [code1] });
const result = await service.search({ languageItemCodes: [code1] }, user);

// Then
assertSingleResult(result, asset);
Expand All @@ -293,7 +304,7 @@ describe(AssetSearchService, () => {
await create({ patch: { ...fakeAssetPatch(), assetKindItemCode: code3 }, user });

// When
const result = await service.search({ assetKindItemCodes: [code1] });
const result = await service.search({ assetKindItemCodes: [code1] }, user);

// Then
assertSingleResult(result, asset);
Expand All @@ -310,7 +321,7 @@ describe(AssetSearchService, () => {
await create({ patch: { ...fakeAssetPatch(), manCatLabelRefs: [code3] }, user });

// When
const result = await service.search({ manCatLabelItemCodes: [code1] });
const result = await service.search({ manCatLabelItemCodes: [code1] }, user);

// Then
assertSingleResult(result, asset);
Expand Down Expand Up @@ -346,7 +357,7 @@ describe(AssetSearchService, () => {
});

// When
const result = await service.search({ usageCodes: [usageCode] });
const result = await service.search({ usageCodes: [usageCode] }, user);

// Then
assertSingleResult(result, asset);
Expand All @@ -371,7 +382,7 @@ describe(AssetSearchService, () => {
});

// When
const result = await service.search({ authorId: contact1.contactId });
const result = await service.search({ authorId: contact1.contactId }, user);

// Then
assertSingleResult(result, asset);
Expand Down Expand Up @@ -417,8 +428,11 @@ describe(AssetSearchService, () => {
};

it('returns empty stats when no assets are present', async () => {
// Given
const user = fakeUser();

// When
const result = await service.aggregate({});
const result = await service.aggregate({}, user);

// Then
expect(result.total).toEqual(0);
Expand All @@ -436,7 +450,7 @@ describe(AssetSearchService, () => {
const asset = await create({ patch: fakeAssetPatch(), user });

// When
const result = await service.aggregate({});
const result = await service.aggregate({}, user);

// Then
assertSingleStats(result, asset);
Expand Down
Loading