Compare commits

..

1 Commits

Author SHA1 Message Date
Liyas Thomas
1dcfc684ef feat: revamped header wireframe 2023-12-01 19:49:23 +05:30
18 changed files with 355 additions and 1332 deletions

View File

@@ -1,5 +0,0 @@
-- AlterTable
ALTER TABLE "TeamCollection" ADD COLUMN "data" JSONB;
-- AlterTable
ALTER TABLE "UserCollection" ADD COLUMN "data" JSONB;

View File

@@ -43,7 +43,6 @@ model TeamInvitation {
model TeamCollection { model TeamCollection {
id String @id @default(cuid()) id String @id @default(cuid())
parentID String? parentID String?
data Json?
parent TeamCollection? @relation("TeamCollectionChildParent", fields: [parentID], references: [id]) parent TeamCollection? @relation("TeamCollectionChildParent", fields: [parentID], references: [id])
children TeamCollection[] @relation("TeamCollectionChildParent") children TeamCollection[] @relation("TeamCollectionChildParent")
requests TeamRequest[] requests TeamRequest[]
@@ -75,8 +74,7 @@ model Shortcode {
creatorUid String? creatorUid String?
User User? @relation(fields: [creatorUid], references: [uid]) User User? @relation(fields: [creatorUid], references: [uid])
createdOn DateTime @default(now()) createdOn DateTime @default(now())
updatedOn DateTime @default(now()) @updatedAt updatedOn DateTime @updatedAt @default(now())
@@unique(fields: [id, creatorUid], name: "creator_uid_shortcode_unique") @@unique(fields: [id, creatorUid], name: "creator_uid_shortcode_unique")
} }
@@ -197,7 +195,6 @@ model UserCollection {
userUid String userUid String
user User @relation(fields: [userUid], references: [uid], onDelete: Cascade) user User @relation(fields: [userUid], references: [uid], onDelete: Cascade)
title String title String
data Json?
orderIndex Int orderIndex Int
type ReqType type ReqType
createdOn DateTime @default(now()) @db.Timestamp(3) createdOn DateTime @default(now()) @db.Timestamp(3)

View File

@@ -254,13 +254,6 @@ export const TEAM_COLL_INVALID_JSON = 'team_coll/invalid_json';
*/ */
export const TEAM_NOT_OWNER = 'team_coll/team_not_owner' as const; export const TEAM_NOT_OWNER = 'team_coll/team_not_owner' as const;
/**
* The Team Collection data is not valid
* (TeamCollectionService)
*/
export const TEAM_COLL_DATA_INVALID =
'team_coll/team_coll_data_invalid' as const;
/** /**
* Tried to perform an action on a request that doesn't accept their member role level * Tried to perform an action on a request that doesn't accept their member role level
* (GqlRequestTeamMemberGuard) * (GqlRequestTeamMemberGuard)
@@ -592,13 +585,6 @@ export const USER_COLL_REORDERING_FAILED =
export const USER_COLL_SAME_NEXT_COLL = export const USER_COLL_SAME_NEXT_COLL =
'user_coll/user_collection_and_next_user_collection_are_same' as const; 'user_coll/user_collection_and_next_user_collection_are_same' as const;
/**
* The User Collection data is not valid
* (UserCollectionService)
*/
export const USER_COLL_DATA_INVALID =
'user_coll/user_coll_data_invalid' as const;
/** /**
* The User Collection does not belong to the logged-in user * The User Collection does not belong to the logged-in user
* (UserCollectionService) * (UserCollectionService)

View File

@@ -21,8 +21,8 @@ import {
} from 'src/team-request/team-request.model'; } from 'src/team-request/team-request.model';
import { TeamInvitation } from 'src/team-invitation/team-invitation.model'; import { TeamInvitation } from 'src/team-invitation/team-invitation.model';
import { InvitedUser } from '../admin/invited-user.model'; import { InvitedUser } from '../admin/invited-user.model';
import { UserCollection } from '@prisma/client';
import { import {
UserCollection,
UserCollectionRemovedData, UserCollectionRemovedData,
UserCollectionReorderData, UserCollectionReorderData,
} from 'src/user-collection/user-collections.model'; } from 'src/user-collection/user-collections.model';

View File

@@ -14,13 +14,6 @@ export class CreateRootTeamCollectionArgs {
@Field({ name: 'title', description: 'Title of the new collection' }) @Field({ name: 'title', description: 'Title of the new collection' })
title: string; title: string;
@Field({
name: 'data',
description: 'JSON string representing the collection data',
nullable: true,
})
data: string;
} }
@ArgsType() @ArgsType()
@@ -33,13 +26,6 @@ export class CreateChildTeamCollectionArgs {
@Field({ name: 'childTitle', description: 'Title of the new collection' }) @Field({ name: 'childTitle', description: 'Title of the new collection' })
childTitle: string; childTitle: string;
@Field({
name: 'data',
description: 'JSON string representing the collection data',
nullable: true,
})
data: string;
} }
@ArgsType() @ArgsType()
@@ -47,14 +33,12 @@ export class RenameTeamCollectionArgs {
@Field(() => ID, { @Field(() => ID, {
name: 'collectionID', name: 'collectionID',
description: 'ID of the collection', description: 'ID of the collection',
deprecationReason: 'Switch to updateTeamCollection mutation instead',
}) })
collectionID: string; collectionID: string;
@Field({ @Field({
name: 'newTitle', name: 'newTitle',
description: 'The updated title of the collection', description: 'The updated title of the collection',
deprecationReason: 'Switch to updateTeamCollection mutation instead',
}) })
newTitle: string; newTitle: string;
} }
@@ -114,26 +98,3 @@ export class ReplaceTeamCollectionArgs {
}) })
parentCollectionID?: string; parentCollectionID?: string;
} }
@ArgsType()
export class UpdateTeamCollectionArgs {
@Field(() => ID, {
name: 'collectionID',
description: 'ID of the collection',
})
collectionID: string;
@Field({
name: 'newTitle',
description: 'The updated title of the collection',
nullable: true,
})
newTitle: string;
@Field({
name: 'data',
description: 'JSON string representing the collection data',
nullable: true,
})
data: string;
}

View File

@@ -12,17 +12,12 @@ export class TeamCollection {
}) })
title: string; title: string;
@Field({
description: 'JSON string representing the collection data',
nullable: true,
})
data: string;
@Field(() => ID, { @Field(() => ID, {
description: 'ID of the collection', description: 'ID of the collection',
nullable: true, nullable: true,
}) })
parentID: string; parentID: string;
teamID: string;
} }
@ObjectType() @ObjectType()

View File

@@ -25,7 +25,6 @@ import {
MoveTeamCollectionArgs, MoveTeamCollectionArgs,
RenameTeamCollectionArgs, RenameTeamCollectionArgs,
ReplaceTeamCollectionArgs, ReplaceTeamCollectionArgs,
UpdateTeamCollectionArgs,
UpdateTeamCollectionOrderArgs, UpdateTeamCollectionOrderArgs,
} from './input-type.args'; } from './input-type.args';
import * as E from 'fp-ts/Either'; import * as E from 'fp-ts/Either';
@@ -142,14 +141,7 @@ export class TeamCollectionResolver {
); );
if (E.isLeft(teamCollections)) throwErr(teamCollections.left); if (E.isLeft(teamCollections)) throwErr(teamCollections.left);
return <TeamCollection>{ return teamCollections.right;
id: teamCollections.right.id,
title: teamCollections.right.title,
parentID: teamCollections.right.parentID,
data: !teamCollections.right.data
? null
: JSON.stringify(teamCollections.right.data),
};
} }
// Mutations // Mutations
@@ -163,7 +155,6 @@ export class TeamCollectionResolver {
const teamCollection = await this.teamCollectionService.createCollection( const teamCollection = await this.teamCollectionService.createCollection(
args.teamID, args.teamID,
args.title, args.title,
args.data,
null, null,
); );
@@ -239,7 +230,6 @@ export class TeamCollectionResolver {
const teamCollection = await this.teamCollectionService.createCollection( const teamCollection = await this.teamCollectionService.createCollection(
team.right.id, team.right.id,
args.childTitle, args.childTitle,
args.data,
args.collectionID, args.collectionID,
); );
@@ -249,7 +239,6 @@ export class TeamCollectionResolver {
@Mutation(() => TeamCollection, { @Mutation(() => TeamCollection, {
description: 'Rename a collection', description: 'Rename a collection',
deprecationReason: 'Switch to updateTeamCollection mutation instead',
}) })
@UseGuards(GqlAuthGuard, GqlCollectionTeamMemberGuard) @UseGuards(GqlAuthGuard, GqlCollectionTeamMemberGuard)
@RequiresTeamRole(TeamMemberRole.OWNER, TeamMemberRole.EDITOR) @RequiresTeamRole(TeamMemberRole.OWNER, TeamMemberRole.EDITOR)
@@ -314,23 +303,6 @@ export class TeamCollectionResolver {
return request.right; return request.right;
} }
@Mutation(() => TeamCollection, {
description: 'Update Team Collection details',
})
@UseGuards(GqlAuthGuard, GqlCollectionTeamMemberGuard)
@RequiresTeamRole(TeamMemberRole.OWNER, TeamMemberRole.EDITOR)
async updateTeamCollection(@Args() args: UpdateTeamCollectionArgs) {
const updatedTeamCollection =
await this.teamCollectionService.updateTeamCollection(
args.collectionID,
args.data,
args.newTitle,
);
if (E.isLeft(updatedTeamCollection)) throwErr(updatedTeamCollection.left);
return updatedTeamCollection.right;
}
// Subscriptions // Subscriptions
@Subscription(() => TeamCollection, { @Subscription(() => TeamCollection, {

View File

@@ -1,7 +1,6 @@
import { Team, TeamCollection as DBTeamCollection } from '@prisma/client'; import { Team, TeamCollection as DBTeamCollection } from '@prisma/client';
import { mockDeep, mockReset } from 'jest-mock-extended'; import { mockDeep, mockReset } from 'jest-mock-extended';
import { import {
TEAM_COLL_DATA_INVALID,
TEAM_COLL_DEST_SAME, TEAM_COLL_DEST_SAME,
TEAM_COLL_INVALID_JSON, TEAM_COLL_INVALID_JSON,
TEAM_COLL_IS_PARENT_COLL, TEAM_COLL_IS_PARENT_COLL,
@@ -18,7 +17,6 @@ import { PrismaService } from 'src/prisma/prisma.service';
import { PubSubService } from 'src/pubsub/pubsub.service'; import { PubSubService } from 'src/pubsub/pubsub.service';
import { AuthUser } from 'src/types/AuthUser'; import { AuthUser } from 'src/types/AuthUser';
import { TeamCollectionService } from './team-collection.service'; import { TeamCollectionService } from './team-collection.service';
import { TeamCollection } from './team-collection.model';
const mockPrisma = mockDeep<PrismaService>(); const mockPrisma = mockDeep<PrismaService>();
const mockPubSub = mockDeep<PubSubService>(); const mockPubSub = mockDeep<PubSubService>();
@@ -53,60 +51,35 @@ const rootTeamCollection: DBTeamCollection = {
id: '123', id: '123',
orderIndex: 1, orderIndex: 1,
parentID: null, parentID: null,
data: {},
title: 'Root Collection 1', title: 'Root Collection 1',
teamID: team.id, teamID: team.id,
createdOn: currentTime, createdOn: currentTime,
updatedOn: currentTime, updatedOn: currentTime,
}; };
const rootTeamCollectionsCasted: TeamCollection = {
id: rootTeamCollection.id,
title: rootTeamCollection.title,
parentID: rootTeamCollection.parentID,
data: JSON.stringify(rootTeamCollection.data),
};
const rootTeamCollection_2: DBTeamCollection = { const rootTeamCollection_2: DBTeamCollection = {
id: 'erv', id: 'erv',
orderIndex: 2, orderIndex: 2,
parentID: null, parentID: null,
data: {},
title: 'Root Collection 1', title: 'Root Collection 1',
teamID: team.id, teamID: team.id,
createdOn: currentTime, createdOn: currentTime,
updatedOn: currentTime, updatedOn: currentTime,
}; };
const rootTeamCollection_2Casted: TeamCollection = {
id: 'erv',
parentID: null,
data: JSON.stringify(rootTeamCollection_2.data),
title: 'Root Collection 1',
};
const childTeamCollection: DBTeamCollection = { const childTeamCollection: DBTeamCollection = {
id: 'rfe', id: 'rfe',
orderIndex: 1, orderIndex: 1,
parentID: rootTeamCollection.id, parentID: rootTeamCollection.id,
data: {},
title: 'Child Collection 1', title: 'Child Collection 1',
teamID: team.id, teamID: team.id,
createdOn: currentTime, createdOn: currentTime,
updatedOn: currentTime, updatedOn: currentTime,
}; };
const childTeamCollectionCasted: TeamCollection = {
id: 'rfe',
parentID: rootTeamCollection.id,
data: JSON.stringify(childTeamCollection.data),
title: 'Child Collection 1',
};
const childTeamCollection_2: DBTeamCollection = { const childTeamCollection_2: DBTeamCollection = {
id: 'bgdz', id: 'bgdz',
orderIndex: 1, orderIndex: 1,
data: {},
parentID: rootTeamCollection_2.id, parentID: rootTeamCollection_2.id,
title: 'Child Collection 1', title: 'Child Collection 1',
teamID: team.id, teamID: team.id,
@@ -114,20 +87,11 @@ const childTeamCollection_2: DBTeamCollection = {
updatedOn: currentTime, updatedOn: currentTime,
}; };
const childTeamCollection_2Casted: TeamCollection = {
id: 'bgdz',
data: JSON.stringify(childTeamCollection_2.data),
parentID: rootTeamCollection_2.id,
title: 'Child Collection 1',
};
const rootTeamCollectionList: DBTeamCollection[] = [ const rootTeamCollectionList: DBTeamCollection[] = [
{ {
id: 'fdv', id: 'fdv',
orderIndex: 1, orderIndex: 1,
parentID: null, parentID: null,
data: {},
title: 'Root Collection 1', title: 'Root Collection 1',
teamID: team.id, teamID: team.id,
createdOn: currentTime, createdOn: currentTime,
@@ -138,8 +102,6 @@ const rootTeamCollectionList: DBTeamCollection[] = [
orderIndex: 2, orderIndex: 2,
parentID: null, parentID: null,
title: 'Root Collection 1', title: 'Root Collection 1',
data: {},
teamID: team.id, teamID: team.id,
createdOn: currentTime, createdOn: currentTime,
updatedOn: currentTime, updatedOn: currentTime,
@@ -149,8 +111,6 @@ const rootTeamCollectionList: DBTeamCollection[] = [
orderIndex: 3, orderIndex: 3,
parentID: null, parentID: null,
title: 'Root Collection 1', title: 'Root Collection 1',
data: {},
teamID: team.id, teamID: team.id,
createdOn: currentTime, createdOn: currentTime,
updatedOn: currentTime, updatedOn: currentTime,
@@ -159,8 +119,6 @@ const rootTeamCollectionList: DBTeamCollection[] = [
id: 'bre3', id: 'bre3',
orderIndex: 4, orderIndex: 4,
parentID: null, parentID: null,
data: {},
title: 'Root Collection 1', title: 'Root Collection 1',
teamID: team.id, teamID: team.id,
createdOn: currentTime, createdOn: currentTime,
@@ -171,8 +129,6 @@ const rootTeamCollectionList: DBTeamCollection[] = [
orderIndex: 5, orderIndex: 5,
parentID: null, parentID: null,
title: 'Root Collection 1', title: 'Root Collection 1',
data: {},
teamID: team.id, teamID: team.id,
createdOn: currentTime, createdOn: currentTime,
updatedOn: currentTime, updatedOn: currentTime,
@@ -183,8 +139,6 @@ const rootTeamCollectionList: DBTeamCollection[] = [
parentID: null, parentID: null,
title: 'Root Collection 1', title: 'Root Collection 1',
teamID: team.id, teamID: team.id,
data: {},
createdOn: currentTime, createdOn: currentTime,
updatedOn: currentTime, updatedOn: currentTime,
}, },
@@ -194,8 +148,6 @@ const rootTeamCollectionList: DBTeamCollection[] = [
parentID: null, parentID: null,
title: 'Root Collection 1', title: 'Root Collection 1',
teamID: team.id, teamID: team.id,
data: {},
createdOn: currentTime, createdOn: currentTime,
updatedOn: currentTime, updatedOn: currentTime,
}, },
@@ -204,7 +156,6 @@ const rootTeamCollectionList: DBTeamCollection[] = [
orderIndex: 8, orderIndex: 8,
parentID: null, parentID: null,
title: 'Root Collection 1', title: 'Root Collection 1',
data: {},
teamID: team.id, teamID: team.id,
createdOn: currentTime, createdOn: currentTime,
updatedOn: currentTime, updatedOn: currentTime,
@@ -214,7 +165,6 @@ const rootTeamCollectionList: DBTeamCollection[] = [
orderIndex: 9, orderIndex: 9,
parentID: null, parentID: null,
title: 'Root Collection 1', title: 'Root Collection 1',
data: {},
teamID: team.id, teamID: team.id,
createdOn: currentTime, createdOn: currentTime,
updatedOn: currentTime, updatedOn: currentTime,
@@ -225,83 +175,17 @@ const rootTeamCollectionList: DBTeamCollection[] = [
parentID: null, parentID: null,
title: 'Root Collection 1', title: 'Root Collection 1',
teamID: team.id, teamID: team.id,
data: {},
createdOn: currentTime, createdOn: currentTime,
updatedOn: currentTime, updatedOn: currentTime,
}, },
]; ];
const rootTeamCollectionListCasted: TeamCollection[] = [
{
id: 'fdv',
parentID: null,
title: 'Root Collection 1',
data: JSON.stringify(rootTeamCollection.data),
},
{
id: 'fbbg',
parentID: null,
title: 'Root Collection 1',
data: JSON.stringify(rootTeamCollection.data),
},
{
id: 'fgbfg',
parentID: null,
title: 'Root Collection 1',
data: JSON.stringify(rootTeamCollection.data),
},
{
id: 'bre3',
parentID: null,
data: JSON.stringify(rootTeamCollection.data),
title: 'Root Collection 1',
},
{
id: 'hghgf',
parentID: null,
title: 'Root Collection 1',
data: JSON.stringify(rootTeamCollection.data),
},
{
id: '123',
parentID: null,
title: 'Root Collection 1',
data: JSON.stringify(rootTeamCollection.data),
},
{
id: '54tyh',
parentID: null,
title: 'Root Collection 1',
data: JSON.stringify(rootTeamCollection.data),
},
{
id: '234re',
parentID: null,
title: 'Root Collection 1',
data: JSON.stringify(rootTeamCollection.data),
},
{
id: '34rtg',
parentID: null,
title: 'Root Collection 1',
data: JSON.stringify(rootTeamCollection.data),
},
{
id: '45tgh',
parentID: null,
title: 'Root Collection 1',
data: JSON.stringify(rootTeamCollection.data),
},
];
const childTeamCollectionList: DBTeamCollection[] = [ const childTeamCollectionList: DBTeamCollection[] = [
{ {
id: '123', id: '123',
orderIndex: 1, orderIndex: 1,
parentID: rootTeamCollection.id, parentID: rootTeamCollection.id,
title: 'Root Collection 1', title: 'Root Collection 1',
data: {},
teamID: team.id, teamID: team.id,
createdOn: currentTime, createdOn: currentTime,
updatedOn: currentTime, updatedOn: currentTime,
@@ -311,8 +195,6 @@ const childTeamCollectionList: DBTeamCollection[] = [
orderIndex: 2, orderIndex: 2,
parentID: rootTeamCollection.id, parentID: rootTeamCollection.id,
title: 'Root Collection 1', title: 'Root Collection 1',
data: {},
teamID: team.id, teamID: team.id,
createdOn: currentTime, createdOn: currentTime,
updatedOn: currentTime, updatedOn: currentTime,
@@ -322,8 +204,6 @@ const childTeamCollectionList: DBTeamCollection[] = [
orderIndex: 3, orderIndex: 3,
parentID: rootTeamCollection.id, parentID: rootTeamCollection.id,
title: 'Root Collection 1', title: 'Root Collection 1',
data: {},
teamID: team.id, teamID: team.id,
createdOn: currentTime, createdOn: currentTime,
updatedOn: currentTime, updatedOn: currentTime,
@@ -332,8 +212,6 @@ const childTeamCollectionList: DBTeamCollection[] = [
id: '567', id: '567',
orderIndex: 4, orderIndex: 4,
parentID: rootTeamCollection.id, parentID: rootTeamCollection.id,
data: {},
title: 'Root Collection 1', title: 'Root Collection 1',
teamID: team.id, teamID: team.id,
createdOn: currentTime, createdOn: currentTime,
@@ -343,8 +221,6 @@ const childTeamCollectionList: DBTeamCollection[] = [
id: '123', id: '123',
orderIndex: 5, orderIndex: 5,
parentID: rootTeamCollection.id, parentID: rootTeamCollection.id,
data: {},
title: 'Root Collection 1', title: 'Root Collection 1',
teamID: team.id, teamID: team.id,
createdOn: currentTime, createdOn: currentTime,
@@ -354,8 +230,6 @@ const childTeamCollectionList: DBTeamCollection[] = [
id: '678', id: '678',
orderIndex: 6, orderIndex: 6,
parentID: rootTeamCollection.id, parentID: rootTeamCollection.id,
data: {},
title: 'Root Collection 1', title: 'Root Collection 1',
teamID: team.id, teamID: team.id,
createdOn: currentTime, createdOn: currentTime,
@@ -365,8 +239,6 @@ const childTeamCollectionList: DBTeamCollection[] = [
id: '789', id: '789',
orderIndex: 7, orderIndex: 7,
parentID: rootTeamCollection.id, parentID: rootTeamCollection.id,
data: {},
title: 'Root Collection 1', title: 'Root Collection 1',
teamID: team.id, teamID: team.id,
createdOn: currentTime, createdOn: currentTime,
@@ -376,8 +248,6 @@ const childTeamCollectionList: DBTeamCollection[] = [
id: '890', id: '890',
orderIndex: 8, orderIndex: 8,
parentID: rootTeamCollection.id, parentID: rootTeamCollection.id,
data: {},
title: 'Root Collection 1', title: 'Root Collection 1',
teamID: team.id, teamID: team.id,
createdOn: currentTime, createdOn: currentTime,
@@ -387,7 +257,6 @@ const childTeamCollectionList: DBTeamCollection[] = [
id: '012', id: '012',
orderIndex: 9, orderIndex: 9,
parentID: rootTeamCollection.id, parentID: rootTeamCollection.id,
data: {},
title: 'Root Collection 1', title: 'Root Collection 1',
teamID: team.id, teamID: team.id,
createdOn: currentTime, createdOn: currentTime,
@@ -397,8 +266,6 @@ const childTeamCollectionList: DBTeamCollection[] = [
id: '0bhu', id: '0bhu',
orderIndex: 10, orderIndex: 10,
parentID: rootTeamCollection.id, parentID: rootTeamCollection.id,
data: {},
title: 'Root Collection 1', title: 'Root Collection 1',
teamID: team.id, teamID: team.id,
createdOn: currentTime, createdOn: currentTime,
@@ -406,75 +273,6 @@ const childTeamCollectionList: DBTeamCollection[] = [
}, },
]; ];
const childTeamCollectionListCasted: TeamCollection[] = [
{
id: '123',
parentID: rootTeamCollection.id,
title: 'Root Collection 1',
data: JSON.stringify({}),
},
{
id: '345',
parentID: rootTeamCollection.id,
title: 'Root Collection 1',
data: JSON.stringify({}),
},
{
id: '456',
parentID: rootTeamCollection.id,
title: 'Root Collection 1',
data: JSON.stringify({}),
},
{
id: '567',
parentID: rootTeamCollection.id,
data: JSON.stringify({}),
title: 'Root Collection 1',
},
{
id: '123',
parentID: rootTeamCollection.id,
data: JSON.stringify({}),
title: 'Root Collection 1',
},
{
id: '678',
parentID: rootTeamCollection.id,
data: JSON.stringify({}),
title: 'Root Collection 1',
},
{
id: '789',
parentID: rootTeamCollection.id,
data: JSON.stringify({}),
title: 'Root Collection 1',
},
{
id: '890',
parentID: rootTeamCollection.id,
data: JSON.stringify({}),
title: 'Root Collection 1',
},
{
id: '012',
parentID: rootTeamCollection.id,
data: JSON.stringify({}),
title: 'Root Collection 1',
},
{
id: '0bhu',
parentID: rootTeamCollection.id,
data: JSON.stringify({}),
title: 'Root Collection 1',
},
];
beforeEach(() => { beforeEach(() => {
mockReset(mockPrisma); mockReset(mockPrisma);
mockPubSub.publish.mockClear(); mockPubSub.publish.mockClear();
@@ -513,7 +311,7 @@ describe('getParentOfCollection', () => {
const result = await teamCollectionService.getParentOfCollection( const result = await teamCollectionService.getParentOfCollection(
childTeamCollection.id, childTeamCollection.id,
); );
expect(result).toEqual(rootTeamCollectionsCasted); expect(result).toEqual(rootTeamCollection);
}); });
test('should return null successfully for a root collection with valid collectionID', async () => { test('should return null successfully for a root collection with valid collectionID', async () => {
@@ -549,7 +347,7 @@ describe('getChildrenOfCollection', () => {
null, null,
10, 10,
); );
expect(result).toEqual(childTeamCollectionListCasted); expect(result).toEqual(childTeamCollectionList);
}); });
test('should return a list of 3 child collections successfully with cursor being equal to the 7th item in the list', async () => { test('should return a list of 3 child collections successfully with cursor being equal to the 7th item in the list', async () => {
@@ -565,9 +363,9 @@ describe('getChildrenOfCollection', () => {
10, 10,
); );
expect(result).toEqual([ expect(result).toEqual([
{ ...childTeamCollectionListCasted[7] }, { ...childTeamCollectionList[7] },
{ ...childTeamCollectionListCasted[8] }, { ...childTeamCollectionList[8] },
{ ...childTeamCollectionListCasted[9] }, { ...childTeamCollectionList[9] },
]); ]);
}); });
@@ -594,7 +392,7 @@ describe('getTeamRootCollections', () => {
null, null,
10, 10,
); );
expect(result).toEqual(rootTeamCollectionListCasted); expect(result).toEqual(rootTeamCollectionList);
}); });
test('should return a list of 3 root collections successfully with cursor being equal to the 7th item in the list', async () => { test('should return a list of 3 root collections successfully with cursor being equal to the 7th item in the list', async () => {
@@ -610,9 +408,9 @@ describe('getTeamRootCollections', () => {
10, 10,
); );
expect(result).toEqual([ expect(result).toEqual([
{ ...rootTeamCollectionListCasted[7] }, { ...rootTeamCollectionList[7] },
{ ...rootTeamCollectionListCasted[8] }, { ...rootTeamCollectionList[8] },
{ ...rootTeamCollectionListCasted[9] }, { ...rootTeamCollectionList[9] },
]); ]);
}); });
@@ -666,7 +464,6 @@ describe('createCollection', () => {
const result = await teamCollectionService.createCollection( const result = await teamCollectionService.createCollection(
rootTeamCollection.teamID, rootTeamCollection.teamID,
'ab', 'ab',
JSON.stringify(rootTeamCollection.data),
rootTeamCollection.id, rootTeamCollection.id,
); );
expect(result).toEqualLeft(TEAM_COLL_SHORT_TITLE); expect(result).toEqualLeft(TEAM_COLL_SHORT_TITLE);
@@ -681,27 +478,11 @@ describe('createCollection', () => {
const result = await teamCollectionService.createCollection( const result = await teamCollectionService.createCollection(
rootTeamCollection.teamID, rootTeamCollection.teamID,
'abcd', 'abcd',
JSON.stringify(rootTeamCollection.data),
rootTeamCollection.id, rootTeamCollection.id,
); );
expect(result).toEqualLeft(TEAM_NOT_OWNER); expect(result).toEqualLeft(TEAM_NOT_OWNER);
}); });
test('should throw TEAM_COLL_DATA_INVALID when parent TeamCollection does not belong to the team', async () => {
// isOwnerCheck
mockPrisma.teamCollection.findFirstOrThrow.mockResolvedValueOnce(
rootTeamCollection,
);
const result = await teamCollectionService.createCollection(
rootTeamCollection.teamID,
'abcd',
'{',
rootTeamCollection.id,
);
expect(result).toEqualLeft(TEAM_COLL_DATA_INVALID);
});
test('should successfully create a new root TeamCollection with valid inputs', async () => { test('should successfully create a new root TeamCollection with valid inputs', async () => {
// isOwnerCheck // isOwnerCheck
mockPrisma.teamCollection.findFirstOrThrow.mockResolvedValueOnce( mockPrisma.teamCollection.findFirstOrThrow.mockResolvedValueOnce(
@@ -715,10 +496,9 @@ describe('createCollection', () => {
const result = await teamCollectionService.createCollection( const result = await teamCollectionService.createCollection(
rootTeamCollection.teamID, rootTeamCollection.teamID,
'abcdefg', 'abcdefg',
JSON.stringify(rootTeamCollection.data),
rootTeamCollection.id, rootTeamCollection.id,
); );
expect(result).toEqualRight(rootTeamCollectionsCasted); expect(result).toEqualRight(rootTeamCollection);
}); });
test('should successfully create a new child TeamCollection with valid inputs', async () => { test('should successfully create a new child TeamCollection with valid inputs', async () => {
@@ -734,10 +514,9 @@ describe('createCollection', () => {
const result = await teamCollectionService.createCollection( const result = await teamCollectionService.createCollection(
childTeamCollection.teamID, childTeamCollection.teamID,
childTeamCollection.title, childTeamCollection.title,
JSON.stringify(rootTeamCollection.data),
rootTeamCollection.id, rootTeamCollection.id,
); );
expect(result).toEqualRight(childTeamCollectionCasted); expect(result).toEqualRight(childTeamCollection);
}); });
test('should send pubsub message to "team_coll/<teamID>/coll_added" if child TeamCollection is created successfully', async () => { test('should send pubsub message to "team_coll/<teamID>/coll_added" if child TeamCollection is created successfully', async () => {
@@ -753,13 +532,11 @@ describe('createCollection', () => {
const result = await teamCollectionService.createCollection( const result = await teamCollectionService.createCollection(
childTeamCollection.teamID, childTeamCollection.teamID,
childTeamCollection.title, childTeamCollection.title,
JSON.stringify(rootTeamCollection.data),
rootTeamCollection.id, rootTeamCollection.id,
); );
expect(mockPubSub.publish).toHaveBeenCalledWith( expect(mockPubSub.publish).toHaveBeenCalledWith(
`team_coll/${childTeamCollection.teamID}/coll_added`, `team_coll/${childTeamCollection.teamID}/coll_added`,
childTeamCollectionCasted, childTeamCollection,
); );
}); });
@@ -776,13 +553,11 @@ describe('createCollection', () => {
const result = await teamCollectionService.createCollection( const result = await teamCollectionService.createCollection(
rootTeamCollection.teamID, rootTeamCollection.teamID,
'abcdefg', 'abcdefg',
JSON.stringify(rootTeamCollection.data),
rootTeamCollection.id, rootTeamCollection.id,
); );
expect(mockPubSub.publish).toHaveBeenCalledWith( expect(mockPubSub.publish).toHaveBeenCalledWith(
`team_coll/${rootTeamCollection.teamID}/coll_added`, `team_coll/${rootTeamCollection.teamID}/coll_added`,
rootTeamCollectionsCasted, rootTeamCollection,
); );
}); });
}); });
@@ -812,7 +587,7 @@ describe('renameCollection', () => {
'NewTitle', 'NewTitle',
); );
expect(result).toEqualRight({ expect(result).toEqualRight({
...rootTeamCollectionsCasted, ...rootTeamCollection,
title: 'NewTitle', title: 'NewTitle',
}); });
}); });
@@ -850,7 +625,7 @@ describe('renameCollection', () => {
expect(mockPubSub.publish).toHaveBeenCalledWith( expect(mockPubSub.publish).toHaveBeenCalledWith(
`team_coll/${rootTeamCollection.teamID}/coll_updated`, `team_coll/${rootTeamCollection.teamID}/coll_updated`,
{ {
...rootTeamCollectionsCasted, ...rootTeamCollection,
title: 'NewTitle', title: 'NewTitle',
}, },
); );
@@ -1057,8 +832,9 @@ describe('moveCollection', () => {
null, null,
); );
expect(result).toEqualRight({ expect(result).toEqualRight({
...childTeamCollectionCasted, ...childTeamCollection,
parentID: null, parentID: null,
orderIndex: 2,
}); });
}); });
@@ -1114,8 +890,9 @@ describe('moveCollection', () => {
expect(mockPubSub.publish).toHaveBeenCalledWith( expect(mockPubSub.publish).toHaveBeenCalledWith(
`team_coll/${childTeamCollection.teamID}/coll_moved`, `team_coll/${childTeamCollection.teamID}/coll_moved`,
{ {
...childTeamCollectionCasted, ...childTeamCollection,
parentID: null, parentID: null,
orderIndex: 2,
}, },
); );
}); });
@@ -1154,8 +931,9 @@ describe('moveCollection', () => {
childTeamCollection_2.id, childTeamCollection_2.id,
); );
expect(result).toEqualRight({ expect(result).toEqualRight({
...rootTeamCollectionsCasted, ...rootTeamCollection,
parentID: childTeamCollection_2Casted.id, parentID: childTeamCollection_2.id,
orderIndex: 1,
}); });
}); });
@@ -1195,8 +973,9 @@ describe('moveCollection', () => {
expect(mockPubSub.publish).toHaveBeenCalledWith( expect(mockPubSub.publish).toHaveBeenCalledWith(
`team_coll/${childTeamCollection_2.teamID}/coll_moved`, `team_coll/${childTeamCollection_2.teamID}/coll_moved`,
{ {
...rootTeamCollectionsCasted, ...rootTeamCollection,
parentID: childTeamCollection_2Casted.id, parentID: childTeamCollection_2.id,
orderIndex: 1,
}, },
); );
}); });
@@ -1235,8 +1014,9 @@ describe('moveCollection', () => {
childTeamCollection_2.id, childTeamCollection_2.id,
); );
expect(result).toEqualRight({ expect(result).toEqualRight({
...childTeamCollectionCasted, ...childTeamCollection,
parentID: childTeamCollection_2Casted.id, parentID: childTeamCollection_2.id,
orderIndex: 1,
}); });
}); });
@@ -1276,8 +1056,9 @@ describe('moveCollection', () => {
expect(mockPubSub.publish).toHaveBeenCalledWith( expect(mockPubSub.publish).toHaveBeenCalledWith(
`team_coll/${childTeamCollection.teamID}/coll_moved`, `team_coll/${childTeamCollection.teamID}/coll_moved`,
{ {
...childTeamCollectionCasted, ...childTeamCollection,
parentID: childTeamCollection_2Casted.id, parentID: childTeamCollection_2.id,
orderIndex: 1,
}, },
); );
}); });
@@ -1373,7 +1154,7 @@ describe('updateCollectionOrder', () => {
expect(mockPubSub.publish).toHaveBeenCalledWith( expect(mockPubSub.publish).toHaveBeenCalledWith(
`team_coll/${childTeamCollectionList[4].teamID}/coll_order_updated`, `team_coll/${childTeamCollectionList[4].teamID}/coll_order_updated`,
{ {
collection: rootTeamCollectionListCasted[4], collection: rootTeamCollectionList[4],
nextCollection: null, nextCollection: null,
}, },
); );
@@ -1454,8 +1235,8 @@ describe('updateCollectionOrder', () => {
expect(mockPubSub.publish).toHaveBeenCalledWith( expect(mockPubSub.publish).toHaveBeenCalledWith(
`team_coll/${childTeamCollectionList[2].teamID}/coll_order_updated`, `team_coll/${childTeamCollectionList[2].teamID}/coll_order_updated`,
{ {
collection: childTeamCollectionListCasted[4], collection: childTeamCollectionList[4],
nextCollection: childTeamCollectionListCasted[2], nextCollection: childTeamCollectionList[2],
}, },
); );
}); });
@@ -1521,7 +1302,7 @@ describe('importCollectionsFromJSON', () => {
); );
expect(mockPubSub.publish).toHaveBeenCalledWith( expect(mockPubSub.publish).toHaveBeenCalledWith(
`team_coll/${rootTeamCollection.teamID}/coll_added`, `team_coll/${rootTeamCollection.teamID}/coll_added`,
rootTeamCollectionsCasted, rootTeamCollection,
); );
}); });
}); });
@@ -1640,7 +1421,7 @@ describe('replaceCollectionsWithJSON', () => {
); );
expect(mockPubSub.publish).toHaveBeenCalledWith( expect(mockPubSub.publish).toHaveBeenCalledWith(
`team_coll/${rootTeamCollection.teamID}/coll_added`, `team_coll/${rootTeamCollection.teamID}/coll_added`,
rootTeamCollectionsCasted, rootTeamCollection,
); );
}); });
}); });
@@ -1677,64 +1458,4 @@ describe('totalCollectionsInTeam', () => {
}); });
}); });
describe('updateTeamCollection', () => {
test('should throw TEAM_COLL_SHORT_TITLE if title is invalid', async () => {
const result = await teamCollectionService.updateTeamCollection(
rootTeamCollection.id,
JSON.stringify(rootTeamCollection.data),
'de',
);
expect(result).toEqualLeft(TEAM_COLL_SHORT_TITLE);
});
test('should throw TEAM_COLL_DATA_INVALID is collection data is invalid', async () => {
const result = await teamCollectionService.updateTeamCollection(
rootTeamCollection.id,
'{',
rootTeamCollection.title,
);
expect(result).toEqualLeft(TEAM_COLL_DATA_INVALID);
});
test('should throw TEAM_COLL_NOT_FOUND is collectionID is invalid', async () => {
mockPrisma.teamCollection.update.mockRejectedValueOnce('RecordNotFound');
const result = await teamCollectionService.updateTeamCollection(
'invalid_id',
JSON.stringify(rootTeamCollection.data),
rootTeamCollection.title,
);
expect(result).toEqualLeft(TEAM_COLL_NOT_FOUND);
});
test('should successfully update a collection', async () => {
mockPrisma.teamCollection.update.mockResolvedValueOnce(rootTeamCollection);
const result = await teamCollectionService.updateTeamCollection(
rootTeamCollection.id,
JSON.stringify({ foo: 'bar' }),
'new_title',
);
expect(result).toEqualRight({
data: JSON.stringify({ foo: 'bar' }),
title: 'new_title',
...rootTeamCollectionsCasted,
});
});
test('should send pubsub message to "team_coll/<teamID>/coll_updated" if TeamCollection is updated successfully', async () => {
mockPrisma.teamCollection.update.mockResolvedValueOnce(rootTeamCollection);
const result = await teamCollectionService.updateTeamCollection(
rootTeamCollection.id,
JSON.stringify(rootTeamCollection.data),
rootTeamCollection.title,
);
expect(mockPubSub.publish).toHaveBeenCalledWith(
`team_coll/${rootTeamCollection.teamID}/coll_updated`,
rootTeamCollectionsCasted,
);
});
});
//ToDo: write test cases for exportCollectionsToJSON //ToDo: write test cases for exportCollectionsToJSON

View File

@@ -13,7 +13,6 @@ import {
TEAM_COLL_IS_PARENT_COLL, TEAM_COLL_IS_PARENT_COLL,
TEAM_COL_SAME_NEXT_COLL, TEAM_COL_SAME_NEXT_COLL,
TEAM_COL_REORDERING_FAILED, TEAM_COL_REORDERING_FAILED,
TEAM_COLL_DATA_INVALID,
} from '../errors'; } from '../errors';
import { PubSubService } from '../pubsub/pubsub.service'; import { PubSubService } from '../pubsub/pubsub.service';
import { isValidLength } from 'src/utils'; import { isValidLength } from 'src/utils';
@@ -70,7 +69,6 @@ export class TeamCollectionService {
this.generatePrismaQueryObjForFBCollFolder(f, teamID, index + 1), this.generatePrismaQueryObjForFBCollFolder(f, teamID, index + 1),
), ),
}, },
data: folder.data ?? undefined,
}; };
} }
@@ -120,7 +118,6 @@ export class TeamCollectionService {
name: collection.right.title, name: collection.right.title,
folders: childrenCollectionObjects, folders: childrenCollectionObjects,
requests: requests.map((x) => x.request), requests: requests.map((x) => x.request),
data: JSON.stringify(collection.right.data),
}; };
return E.right(result); return E.right(result);
@@ -201,11 +198,8 @@ export class TeamCollectionService {
), ),
); );
teamCollections.forEach((collection) => teamCollections.forEach((x) =>
this.pubsub.publish( this.pubsub.publish(`team_coll/${destTeamID}/coll_added`, x),
`team_coll/${destTeamID}/coll_added`,
this.cast(collection),
),
); );
return E.right(true); return E.right(true);
@@ -274,11 +268,8 @@ export class TeamCollectionService {
), ),
); );
teamCollections.forEach((collections) => teamCollections.forEach((x) =>
this.pubsub.publish( this.pubsub.publish(`team_coll/${destTeamID}/coll_added`, x),
`team_coll/${destTeamID}/coll_added`,
this.cast(collections),
),
); );
return E.right(true); return E.right(true);
@@ -286,17 +277,11 @@ export class TeamCollectionService {
/** /**
* Typecast a database TeamCollection to a TeamCollection model * Typecast a database TeamCollection to a TeamCollection model
*
* @param teamCollection database TeamCollection * @param teamCollection database TeamCollection
* @returns TeamCollection model * @returns TeamCollection model
*/ */
private cast(teamCollection: DBTeamCollection): TeamCollection { private cast(teamCollection: DBTeamCollection): TeamCollection {
return <TeamCollection>{ return <TeamCollection>{ ...teamCollection };
id: teamCollection.id,
title: teamCollection.title,
parentID: teamCollection.parentID,
data: !teamCollection.data ? null : JSON.stringify(teamCollection.data),
};
} }
/** /**
@@ -339,7 +324,7 @@ export class TeamCollectionService {
}); });
if (!teamCollection) return null; if (!teamCollection) return null;
return !teamCollection.parent ? null : this.cast(teamCollection.parent); return teamCollection.parent;
} }
/** /**
@@ -350,12 +335,12 @@ export class TeamCollectionService {
* @param take Number of items we want returned * @param take Number of items we want returned
* @returns A list of child collections * @returns A list of child collections
*/ */
async getChildrenOfCollection( getChildrenOfCollection(
collectionID: string, collectionID: string,
cursor: string | null, cursor: string | null,
take: number, take: number,
) { ) {
const res = await this.prisma.teamCollection.findMany({ return this.prisma.teamCollection.findMany({
where: { where: {
parentID: collectionID, parentID: collectionID,
}, },
@@ -366,12 +351,6 @@ export class TeamCollectionService {
skip: cursor ? 1 : 0, skip: cursor ? 1 : 0,
cursor: cursor ? { id: cursor } : undefined, cursor: cursor ? { id: cursor } : undefined,
}); });
const childCollections = res.map((teamCollection) =>
this.cast(teamCollection),
);
return childCollections;
} }
/** /**
@@ -387,7 +366,7 @@ export class TeamCollectionService {
cursor: string | null, cursor: string | null,
take: number, take: number,
) { ) {
const res = await this.prisma.teamCollection.findMany({ return this.prisma.teamCollection.findMany({
where: { where: {
teamID, teamID,
parentID: null, parentID: null,
@@ -399,12 +378,6 @@ export class TeamCollectionService {
skip: cursor ? 1 : 0, skip: cursor ? 1 : 0,
cursor: cursor ? { id: cursor } : undefined, cursor: cursor ? { id: cursor } : undefined,
}); });
const teamCollections = res.map((teamCollection) =>
this.cast(teamCollection),
);
return teamCollections;
} }
/** /**
@@ -497,7 +470,6 @@ export class TeamCollectionService {
async createCollection( async createCollection(
teamID: string, teamID: string,
title: string, title: string,
data: string | null = null,
parentTeamCollectionID: string | null, parentTeamCollectionID: string | null,
) { ) {
const isTitleValid = isValidLength(title, this.TITLE_LENGTH); const isTitleValid = isValidLength(title, this.TITLE_LENGTH);
@@ -509,13 +481,6 @@ export class TeamCollectionService {
if (O.isNone(isOwner)) return E.left(TEAM_NOT_OWNER); if (O.isNone(isOwner)) return E.left(TEAM_NOT_OWNER);
} }
if (data === '') return E.left(TEAM_COLL_DATA_INVALID);
if (data) {
const jsonReq = stringToJson(data);
if (E.isLeft(jsonReq)) return E.left(TEAM_COLL_DATA_INVALID);
data = jsonReq.right;
}
const isParent = parentTeamCollectionID const isParent = parentTeamCollectionID
? { ? {
connect: { connect: {
@@ -533,23 +498,18 @@ export class TeamCollectionService {
}, },
}, },
parent: isParent, parent: isParent,
data: data ?? undefined,
orderIndex: !parentTeamCollectionID orderIndex: !parentTeamCollectionID
? (await this.getRootCollectionsCount(teamID)) + 1 ? (await this.getRootCollectionsCount(teamID)) + 1
: (await this.getChildCollectionsCount(parentTeamCollectionID)) + 1, : (await this.getChildCollectionsCount(parentTeamCollectionID)) + 1,
}, },
}); });
this.pubsub.publish( this.pubsub.publish(`team_coll/${teamID}/coll_added`, teamCollection);
`team_coll/${teamID}/coll_added`,
this.cast(teamCollection),
);
return E.right(this.cast(teamCollection)); return E.right(this.cast(teamCollection));
} }
/** /**
* @deprecated Use updateTeamCollection method instead
* Update the title of a TeamCollection * Update the title of a TeamCollection
* *
* @param collectionID The Collection ID * @param collectionID The Collection ID
@@ -572,10 +532,10 @@ export class TeamCollectionService {
this.pubsub.publish( this.pubsub.publish(
`team_coll/${updatedTeamCollection.teamID}/coll_updated`, `team_coll/${updatedTeamCollection.teamID}/coll_updated`,
this.cast(updatedTeamCollection), updatedTeamCollection,
); );
return E.right(this.cast(updatedTeamCollection)); return E.right(updatedTeamCollection);
} catch (error) { } catch (error) {
return E.left(TEAM_COLL_NOT_FOUND); return E.left(TEAM_COLL_NOT_FOUND);
} }
@@ -734,8 +694,8 @@ export class TeamCollectionService {
* @returns An Option of boolean, is parent or not * @returns An Option of boolean, is parent or not
*/ */
private async isParent( private async isParent(
collection: DBTeamCollection, collection: TeamCollection,
destCollection: DBTeamCollection, destCollection: TeamCollection,
): Promise<O.Option<boolean>> { ): Promise<O.Option<boolean>> {
//* Recursively check if collection is a parent by going up the tree of child-parent collections until we reach a root collection i.e parentID === null //* Recursively check if collection is a parent by going up the tree of child-parent collections until we reach a root collection i.e parentID === null
//* Valid condition, isParent returns false //* Valid condition, isParent returns false
@@ -1011,49 +971,4 @@ export class TeamCollectionService {
const teamCollectionsCount = this.prisma.teamCollection.count(); const teamCollectionsCount = this.prisma.teamCollection.count();
return teamCollectionsCount; return teamCollectionsCount;
} }
/**
* Update Team Collection details
*
* @param collectionID Collection ID
* @param collectionData new header data in a JSONified string form
* @param newTitle New title of the collection
* @returns Updated TeamCollection
*/
async updateTeamCollection(
collectionID: string,
collectionData: string = null,
newTitle: string = null,
) {
try {
if (newTitle != null) {
const isTitleValid = isValidLength(newTitle, this.TITLE_LENGTH);
if (!isTitleValid) return E.left(TEAM_COLL_SHORT_TITLE);
}
if (collectionData === '') return E.left(TEAM_COLL_DATA_INVALID);
if (collectionData) {
const jsonReq = stringToJson(collectionData);
if (E.isLeft(jsonReq)) return E.left(TEAM_COLL_DATA_INVALID);
collectionData = jsonReq.right;
}
const updatedTeamCollection = await this.prisma.teamCollection.update({
where: { id: collectionID },
data: {
data: collectionData ?? undefined,
title: newTitle ?? undefined,
},
});
this.pubsub.publish(
`team_coll/${updatedTeamCollection.teamID}/coll_updated`,
this.cast(updatedTeamCollection),
);
return E.right(this.cast(updatedTeamCollection));
} catch (e) {
return E.left(TEAM_COLL_NOT_FOUND);
}
}
} }

View File

@@ -42,7 +42,6 @@ const teamCollection: DbTeamCollection = {
id: 'team-coll-1', id: 'team-coll-1',
parentID: null, parentID: null,
teamID: team.id, teamID: team.id,
data: {},
title: 'Team Collection 1', title: 'Team Collection 1',
orderIndex: 1, orderIndex: 1,
createdOn: new Date(), createdOn: new Date(),

View File

@@ -1,8 +1,6 @@
// This interface defines how data will be received from the app when we are importing Hoppscotch collections
export interface CollectionFolder { export interface CollectionFolder {
id?: string; id?: string;
folders: CollectionFolder[]; folders: CollectionFolder[];
requests: any[]; requests: any[];
name: string; name: string;
data?: string;
} }

View File

@@ -6,13 +6,6 @@ import { PaginationArgs } from 'src/types/input-types.args';
export class CreateRootUserCollectionArgs { export class CreateRootUserCollectionArgs {
@Field({ name: 'title', description: 'Title of the new user collection' }) @Field({ name: 'title', description: 'Title of the new user collection' })
title: string; title: string;
@Field({
name: 'data',
description: 'JSON string representing the collection data',
nullable: true,
})
data: string;
} }
@ArgsType() @ArgsType()
export class CreateChildUserCollectionArgs { export class CreateChildUserCollectionArgs {
@@ -24,13 +17,6 @@ export class CreateChildUserCollectionArgs {
description: 'ID of the parent to the new user collection', description: 'ID of the parent to the new user collection',
}) })
parentUserCollectionID: string; parentUserCollectionID: string;
@Field({
name: 'data',
description: 'JSON string representing the collection data',
nullable: true,
})
data: string;
} }
@ArgsType() @ArgsType()
@@ -109,26 +95,3 @@ export class ImportUserCollectionsFromJSONArgs {
}) })
parentCollectionID?: string; parentCollectionID?: string;
} }
@ArgsType()
export class UpdateUserCollectionsArgs {
@Field(() => ID, {
name: 'userCollectionID',
description: 'ID of the user collection',
})
userCollectionID: string;
@Field({
name: 'newTitle',
description: 'The updated title of the user collection',
nullable: true,
})
newTitle: string;
@Field({
name: 'data',
description: 'JSON string representing the collection data',
nullable: true,
})
data: string;
}

View File

@@ -30,7 +30,6 @@ import {
MoveUserCollectionArgs, MoveUserCollectionArgs,
RenameUserCollectionsArgs, RenameUserCollectionsArgs,
UpdateUserCollectionArgs, UpdateUserCollectionArgs,
UpdateUserCollectionsArgs,
} from './input-type.args'; } from './input-type.args';
import { ReqType } from 'src/types/RequestTypes'; import { ReqType } from 'src/types/RequestTypes';
import * as E from 'fp-ts/Either'; import * as E from 'fp-ts/Either';
@@ -143,13 +142,7 @@ export class UserCollectionResolver {
); );
if (E.isLeft(userCollection)) throwErr(userCollection.left); if (E.isLeft(userCollection)) throwErr(userCollection.left);
return <UserCollection>{ return userCollection.right;
...userCollection.right,
userID: userCollection.right.userUid,
data: !userCollection.right.data
? null
: JSON.stringify(userCollection.right.data),
};
} }
@Query(() => UserCollectionExportJSONData, { @Query(() => UserCollectionExportJSONData, {
@@ -198,7 +191,6 @@ export class UserCollectionResolver {
await this.userCollectionService.createUserCollection( await this.userCollectionService.createUserCollection(
user, user,
args.title, args.title,
args.data,
null, null,
ReqType.REST, ReqType.REST,
); );
@@ -220,7 +212,6 @@ export class UserCollectionResolver {
await this.userCollectionService.createUserCollection( await this.userCollectionService.createUserCollection(
user, user,
args.title, args.title,
args.data,
null, null,
ReqType.GQL, ReqType.GQL,
); );
@@ -241,7 +232,6 @@ export class UserCollectionResolver {
await this.userCollectionService.createUserCollection( await this.userCollectionService.createUserCollection(
user, user,
args.title, args.title,
args.data,
args.parentUserCollectionID, args.parentUserCollectionID,
ReqType.GQL, ReqType.GQL,
); );
@@ -262,7 +252,6 @@ export class UserCollectionResolver {
await this.userCollectionService.createUserCollection( await this.userCollectionService.createUserCollection(
user, user,
args.title, args.title,
args.data,
args.parentUserCollectionID, args.parentUserCollectionID,
ReqType.REST, ReqType.REST,
); );
@@ -370,26 +359,6 @@ export class UserCollectionResolver {
return importedCollection.right; return importedCollection.right;
} }
@Mutation(() => UserCollection, {
description: 'Update a UserCollection',
})
@UseGuards(GqlAuthGuard)
async updateUserCollection(
@GqlUser() user: AuthUser,
@Args() args: UpdateUserCollectionsArgs,
) {
const updatedUserCollection =
await this.userCollectionService.updateUserCollection(
args.newTitle,
args.data,
args.userCollectionID,
user.uid,
);
if (E.isLeft(updatedUserCollection)) throwErr(updatedUserCollection.left);
return updatedUserCollection.right;
}
// Subscriptions // Subscriptions
@Subscription(() => UserCollection, { @Subscription(() => UserCollection, {
description: 'Listen for User Collection Creation', description: 'Listen for User Collection Creation',

View File

@@ -12,7 +12,6 @@ import {
USER_NOT_FOUND, USER_NOT_FOUND,
USER_NOT_OWNER, USER_NOT_OWNER,
USER_COLL_INVALID_JSON, USER_COLL_INVALID_JSON,
USER_COLL_DATA_INVALID,
} from 'src/errors'; } from 'src/errors';
import { PrismaService } from 'src/prisma/prisma.service'; import { PrismaService } from 'src/prisma/prisma.service';
import { AuthUser } from 'src/types/AuthUser'; import { AuthUser } from 'src/types/AuthUser';
@@ -44,12 +43,8 @@ export class UserCollectionService {
*/ */
private cast(collection: UserCollection) { private cast(collection: UserCollection) {
return <UserCollectionModel>{ return <UserCollectionModel>{
id: collection.id, ...collection,
title: collection.title,
type: collection.type,
parentID: collection.parentID,
userID: collection.userUid, userID: collection.userUid,
data: !collection.data ? null : JSON.stringify(collection.data),
}; };
} }
@@ -151,7 +146,7 @@ export class UserCollectionService {
}, },
}); });
return !parent ? null : this.cast(parent); return parent;
} }
/** /**
@@ -169,7 +164,7 @@ export class UserCollectionService {
take: number, take: number,
type: ReqType, type: ReqType,
) { ) {
const res = await this.prisma.userCollection.findMany({ return this.prisma.userCollection.findMany({
where: { where: {
parentID: collectionID, parentID: collectionID,
type: type, type: type,
@@ -181,12 +176,6 @@ export class UserCollectionService {
skip: cursor ? 1 : 0, skip: cursor ? 1 : 0,
cursor: cursor ? { id: cursor } : undefined, cursor: cursor ? { id: cursor } : undefined,
}); });
const childCollections = res.map((childCollection) =>
this.cast(childCollection),
);
return childCollections;
} }
/** /**
@@ -222,20 +211,12 @@ export class UserCollectionService {
async createUserCollection( async createUserCollection(
user: AuthUser, user: AuthUser,
title: string, title: string,
data: string | null = null,
parentUserCollectionID: string | null, parentUserCollectionID: string | null,
type: ReqType, type: ReqType,
) { ) {
const isTitleValid = isValidLength(title, this.TITLE_LENGTH); const isTitleValid = isValidLength(title, this.TITLE_LENGTH);
if (!isTitleValid) return E.left(USER_COLL_SHORT_TITLE); if (!isTitleValid) return E.left(USER_COLL_SHORT_TITLE);
if (data === '') return E.left(USER_COLL_DATA_INVALID);
if (data) {
const jsonReq = stringToJson(data);
if (E.isLeft(jsonReq)) return E.left(USER_COLL_DATA_INVALID);
data = jsonReq.right;
}
// If creating a child collection // If creating a child collection
if (parentUserCollectionID !== null) { if (parentUserCollectionID !== null) {
const parentCollection = await this.getUserCollection( const parentCollection = await this.getUserCollection(
@@ -270,19 +251,15 @@ export class UserCollectionService {
}, },
}, },
parent: isParent, parent: isParent,
data: data ?? undefined,
orderIndex: !parentUserCollectionID orderIndex: !parentUserCollectionID
? (await this.getRootCollectionsCount(user.uid)) + 1 ? (await this.getRootCollectionsCount(user.uid)) + 1
: (await this.getChildCollectionsCount(parentUserCollectionID)) + 1, : (await this.getChildCollectionsCount(parentUserCollectionID)) + 1,
}, },
}); });
await this.pubsub.publish( await this.pubsub.publish(`user_coll/${user.uid}/created`, userCollection);
`user_coll/${user.uid}/created`,
this.cast(userCollection),
);
return E.right(this.cast(userCollection)); return E.right(userCollection);
} }
/** /**
@@ -299,7 +276,7 @@ export class UserCollectionService {
take: number, take: number,
type: ReqType, type: ReqType,
) { ) {
const res = await this.prisma.userCollection.findMany({ return this.prisma.userCollection.findMany({
where: { where: {
userUid: user.uid, userUid: user.uid,
parentID: null, parentID: null,
@@ -312,12 +289,6 @@ export class UserCollectionService {
skip: cursor ? 1 : 0, skip: cursor ? 1 : 0,
cursor: cursor ? { id: cursor } : undefined, cursor: cursor ? { id: cursor } : undefined,
}); });
const userCollections = res.map((childCollection) =>
this.cast(childCollection),
);
return userCollections;
} }
/** /**
@@ -336,7 +307,7 @@ export class UserCollectionService {
take: number, take: number,
type: ReqType, type: ReqType,
) { ) {
const res = await this.prisma.userCollection.findMany({ return this.prisma.userCollection.findMany({
where: { where: {
userUid: user.uid, userUid: user.uid,
parentID: userCollectionID, parentID: userCollectionID,
@@ -346,16 +317,9 @@ export class UserCollectionService {
skip: cursor ? 1 : 0, skip: cursor ? 1 : 0,
cursor: cursor ? { id: cursor } : undefined, cursor: cursor ? { id: cursor } : undefined,
}); });
const childCollections = res.map((childCollection) =>
this.cast(childCollection),
);
return childCollections;
} }
/** /**
* @deprecated Use updateUserCollection method instead
* Update the title of a UserCollection * Update the title of a UserCollection
* *
* @param newTitle The new title of collection * @param newTitle The new title of collection
@@ -387,10 +351,10 @@ export class UserCollectionService {
this.pubsub.publish( this.pubsub.publish(
`user_coll/${updatedUserCollection.userUid}/updated`, `user_coll/${updatedUserCollection.userUid}/updated`,
this.cast(updatedUserCollection), updatedUserCollection,
); );
return E.right(this.cast(updatedUserCollection)); return E.right(updatedUserCollection);
} catch (error) { } catch (error) {
return E.left(USER_COLL_NOT_FOUND); return E.left(USER_COLL_NOT_FOUND);
} }
@@ -627,10 +591,10 @@ export class UserCollectionService {
this.pubsub.publish( this.pubsub.publish(
`user_coll/${collection.right.userUid}/moved`, `user_coll/${collection.right.userUid}/moved`,
this.cast(updatedCollection.right), updatedCollection.right,
); );
return E.right(this.cast(updatedCollection.right)); return E.right(updatedCollection.right);
} }
// destCollectionID != null i.e move into another collection // destCollectionID != null i.e move into another collection
@@ -678,10 +642,10 @@ export class UserCollectionService {
this.pubsub.publish( this.pubsub.publish(
`user_coll/${collection.right.userUid}/moved`, `user_coll/${collection.right.userUid}/moved`,
this.cast(updatedCollection.right), updatedCollection.right,
); );
return E.right(this.cast(updatedCollection.right)); return E.right(updatedCollection.right);
} }
/** /**
@@ -882,7 +846,6 @@ export class UserCollectionService {
...(x.request as Record<string, unknown>), // type casting x.request of type Prisma.JSONValue to an object to enable spread ...(x.request as Record<string, unknown>), // type casting x.request of type Prisma.JSONValue to an object to enable spread
}; };
}), }),
data: JSON.stringify(collection.right.data),
}; };
return E.right(result); return E.right(result);
@@ -955,7 +918,6 @@ export class UserCollectionService {
...(x.request as Record<string, unknown>), // type casting x.request of type Prisma.JSONValue to an object to enable spread ...(x.request as Record<string, unknown>), // type casting x.request of type Prisma.JSONValue to an object to enable spread
}; };
}), }),
data: JSON.stringify(parentCollection.right.data),
}), }),
collectionType: parentCollection.right.type, collectionType: parentCollection.right.type,
}); });
@@ -1009,7 +971,6 @@ export class UserCollectionService {
this.generatePrismaQueryObj(f, userID, index + 1, reqType), this.generatePrismaQueryObj(f, userID, index + 1, reqType),
), ),
}, },
data: folder.data ?? undefined,
}; };
} }
@@ -1079,63 +1040,10 @@ export class UserCollectionService {
), ),
); );
userCollections.forEach((collection) => userCollections.forEach((x) =>
this.pubsub.publish(`user_coll/${userID}/created`, this.cast(collection)), this.pubsub.publish(`user_coll/${userID}/created`, x),
); );
return E.right(true); return E.right(true);
} }
/**
* Update a UserCollection
*
* @param newTitle The new title of collection
* @param userCollectionID The Collection Id
* @param userID The User UID
* @returns An Either of the updated UserCollection
*/
async updateUserCollection(
newTitle: string = null,
collectionData: string | null = null,
userCollectionID: string,
userID: string,
) {
if (collectionData === '') return E.left(USER_COLL_DATA_INVALID);
if (collectionData) {
const jsonReq = stringToJson(collectionData);
if (E.isLeft(jsonReq)) return E.left(USER_COLL_DATA_INVALID);
collectionData = jsonReq.right;
}
if (newTitle != null) {
const isTitleValid = isValidLength(newTitle, this.TITLE_LENGTH);
if (!isTitleValid) return E.left(USER_COLL_SHORT_TITLE);
}
// Check to see is the collection belongs to the user
const isOwner = await this.isOwnerCheck(userCollectionID, userID);
if (O.isNone(isOwner)) return E.left(USER_NOT_OWNER);
try {
const updatedUserCollection = await this.prisma.userCollection.update({
where: {
id: userCollectionID,
},
data: {
data: collectionData ?? undefined,
title: newTitle ?? undefined,
},
});
this.pubsub.publish(
`user_coll/${updatedUserCollection.userUid}/updated`,
this.cast(updatedUserCollection),
);
return E.right(this.cast(updatedUserCollection));
} catch (error) {
return E.left(USER_COLL_NOT_FOUND);
}
}
} }

View File

@@ -13,12 +13,6 @@ export class UserCollection {
}) })
title: string; title: string;
@Field({
description: 'JSON string representing the collection data',
nullable: true,
})
data: string;
@Field(() => ReqType, { @Field(() => ReqType, {
description: 'Type of the user collection', description: 'Type of the user collection',
}) })

View File

@@ -332,9 +332,13 @@
"url": "URL" "url": "URL"
}, },
"header": { "header": {
"install_pwa": "Install app", "install_pwa": "Add to Home Screen",
"login": "Login", "login": "Login",
"save_workspace": "Save My Workspace" "save_workspace": "Save My Workspace",
"download_app": "Download app",
"menu": "Menu",
"go_back": "Go back",
"go_forward": "Go forward"
}, },
"helpers": { "helpers": {
"authorization": "The authorization header will be automatically generated when you send the request.", "authorization": "The authorization header will be automatically generated when you send the request.",

View File

@@ -2,25 +2,49 @@
<div> <div>
<header <header
ref="headerRef" ref="headerRef"
class="flex flex-1 flex-shrink-0 items-center justify-between space-x-2 overflow-x-auto overflow-y-hidden px-2 py-2" class="grid grid-cols-5 grid-rows-1 gap-2 overflow-x-auto overflow-y-hidden p-2"
@mousedown.prevent="platform.ui?.appHeader?.onHeaderAreaClick?.()" @mousedown.prevent="platform.ui?.appHeader?.onHeaderAreaClick?.()"
> >
<div <div
class="inline-flex flex-1 items-center justify-start space-x-2" class="col-span-2 flex items-center justify-between space-x-2"
:style="{ :style="{
paddingTop: platform.ui?.appHeader?.paddingTop?.value, paddingTop: platform.ui?.appHeader?.paddingTop?.value,
paddingLeft: platform.ui?.appHeader?.paddingLeft?.value, paddingLeft: platform.ui?.appHeader?.paddingLeft?.value,
}" }"
> >
<HoppButtonSecondary <div class="flex">
class="!font-bold uppercase tracking-wide !text-secondaryDark hover:bg-primaryDark focus-visible:bg-primaryDark" <HoppButtonSecondary
:label="t('app.name')" v-tippy="{ theme: 'tooltip' }"
to="/" :title="t('header.menu')"
/> :icon="IconMenu"
class="rounded hover:bg-primaryDark focus-visible:bg-primaryDark"
/>
<HoppButtonSecondary
class="!font-bold uppercase tracking-wide !text-secondaryDark hover:bg-primaryDark focus-visible:bg-primaryDark"
:label="t('app.name')"
to="/"
/>
</div>
<div class="flex">
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="t('header.go_back')"
:icon="IconArrowLeft"
class="rounded hover:bg-primaryDark focus-visible:bg-primaryDark"
@click="router.back()"
/>
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="t('header.go_forward')"
:icon="IconArrowRight"
class="rounded hover:bg-primaryDark focus-visible:bg-primaryDark"
@click="router.forward()"
/>
</div>
</div> </div>
<div class="inline-flex flex-1 items-center justify-center space-x-2"> <div class="col-span-1 flex items-center justify-between space-x-2">
<button <button
class="flex max-w-[15rem] flex-1 cursor-text items-center justify-between self-stretch rounded border border-dividerDark bg-primaryDark px-2 py-1 text-secondaryLight transition hover:border-dividerDark hover:bg-primaryLight hover:text-secondary focus-visible:border-dividerDark focus-visible:bg-primaryLight focus-visible:text-secondary" class="flex h-full flex-1 cursor-text items-center justify-between rounded border border-dividerDark bg-primaryDark px-2 text-secondaryLight transition hover:border-dividerDark hover:bg-primaryLight hover:text-secondary focus-visible:border-dividerDark focus-visible:bg-primaryLight focus-visible:text-secondary"
@click="invokeAction('modals.search.toggle')" @click="invokeAction('modals.search.toggle')"
> >
<span class="inline-flex flex-1 items-center"> <span class="inline-flex flex-1 items-center">
@@ -32,189 +56,221 @@
<kbd class="shortcut-key">K</kbd> <kbd class="shortcut-key">K</kbd>
</span> </span>
</button> </button>
<HoppButtonSecondary
v-if="showInstallButton"
v-tippy="{ theme: 'tooltip' }"
:title="t('header.install_pwa')"
:icon="IconDownload"
class="rounded hover:bg-primaryDark focus-visible:bg-primaryDark"
@click="installPWA()"
/>
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip', allowHTML: true }"
:title="`${
mdAndLarger ? t('support.title') : t('app.options')
} <kbd>?</kbd>`"
:icon="IconLifeBuoy"
class="rounded hover:bg-primaryDark focus-visible:bg-primaryDark"
@click="invokeAction('modals.support.toggle')"
/>
</div> </div>
<div class="inline-flex flex-1 items-center justify-end space-x-2"> <div class="col-span-2 flex items-center justify-between space-x-2">
<div <div class="flex">
v-if="currentUser === null"
class="inline-flex items-center space-x-2"
>
<HoppButtonSecondary <HoppButtonSecondary
:icon="IconUploadCloud" v-tippy="{ theme: 'tooltip', allowHTML: true }"
:label="t('header.save_workspace')" :title="`${
class="py-1.75 !focus-visible:text-green-600 !hover:text-green-600 hidden border border-green-600/25 bg-green-500/[.15] !text-green-500 hover:border-green-800/50 hover:bg-green-400/10 focus-visible:border-green-800/50 focus-visible:bg-green-400/10 md:flex" mdAndLarger ? t('support.title') : t('app.options')
@click="invokeAction('modals.login.toggle')" } <kbd>?</kbd>`"
:icon="IconLifeBuoy"
class="rounded hover:bg-primaryDark focus-visible:bg-primaryDark"
@click="invokeAction('modals.support.toggle')"
/> />
<HoppButtonPrimary <span>
:label="t('header.login')"
@click="invokeAction('modals.login.toggle')"
/>
</div>
<div v-else class="inline-flex items-center space-x-2">
<TeamsMemberStack
v-if="
workspace.type === 'team' &&
selectedTeam &&
selectedTeam.teamMembers.length > 1
"
:team-members="selectedTeam.teamMembers"
show-count
class="mx-2"
@handle-click="handleTeamEdit()"
/>
<div
class="flex divide-x divide-green-600/25 rounded border border-green-600/25 bg-green-500/[.15] focus-within:divide-green-800/50 focus-within:border-green-800/50 focus-within:bg-green-400/10 hover:divide-green-800/50 hover:border-green-800/50 hover:bg-green-400/10"
>
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="t('team.invite_tooltip')"
:icon="IconUserPlus"
class="py-1.75 !focus-visible:text-green-600 !hover:text-green-600 !text-green-500"
@click="handleInvite()"
/>
<HoppButtonSecondary
v-if="
workspace.type === 'team' &&
selectedTeam &&
selectedTeam?.myRole === 'OWNER'
"
v-tippy="{ theme: 'tooltip' }"
:title="t('team.edit')"
:icon="IconSettings"
class="py-1.75 !focus-visible:text-green-600 !hover:text-green-600 !text-green-500"
@click="handleTeamEdit()"
/>
</div>
<tippy
interactive
trigger="click"
theme="popover"
:on-shown="() => accountActions.focus()"
>
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="t('workspace.change')"
:label="mdAndLarger ? workspaceName : ``"
:icon="workspace.type === 'personal' ? IconUser : IconUsers"
class="select-wrapper !focus-visible:text-blue-600 !hover:text-blue-600 rounded border border-blue-600/25 bg-blue-500/[.15] py-[0.4375rem] pr-8 !text-blue-500 hover:border-blue-800/50 hover:bg-blue-400/10 focus-visible:border-blue-800/50 focus-visible:bg-blue-400/10"
/>
<template #content="{ hide }">
<div
ref="accountActions"
class="flex flex-col focus:outline-none"
tabindex="0"
@keyup.escape="hide()"
@click="hide()"
>
<WorkspaceSelector />
</div>
</template>
</tippy>
<span class="px-2">
<tippy <tippy
interactive interactive
trigger="click" trigger="click"
theme="popover" theme="popover"
:on-shown="() => tippyActions.focus()" :on-shown="() => downloadActions.focus()"
> >
<HoppSmartPicture <HoppButtonSecondary
v-if="currentUser.photoURL"
v-tippy="{
theme: 'tooltip',
}"
:url="currentUser.photoURL"
:alt="
currentUser.displayName ||
t('profile.default_hopp_displayname')
"
:title="
currentUser.displayName ||
currentUser.email ||
t('profile.default_hopp_displayname')
"
indicator
:indicator-styles="
network.isOnline ? 'bg-green-500' : 'bg-red-500'
"
/>
<HoppSmartPicture
v-else
v-tippy="{ theme: 'tooltip' }" v-tippy="{ theme: 'tooltip' }"
:title=" :title="t('header.download_app')"
currentUser.displayName || :icon="IconDownload"
currentUser.email || class="rounded hover:bg-primaryDark focus-visible:bg-primaryDark"
t('profile.default_hopp_displayname')
"
:initial="currentUser.displayName || currentUser.email"
indicator
:indicator-styles="
network.isOnline ? 'bg-green-500' : 'bg-red-500'
"
/> />
<template #content="{ hide }"> <template #content="{ hide }">
<div <div
ref="tippyActions" ref="downloadActions"
class="flex flex-col focus:outline-none" class="flex flex-col focus:outline-none"
tabindex="0" tabindex="0"
@keyup.p="profile.$el.click()"
@keyup.s="settings.$el.click()"
@keyup.l="logout.$el.click()"
@keyup.escape="hide()" @keyup.escape="hide()"
@click="hide()"
> >
<div class="flex flex-col px-2 text-tiny">
<span class="inline-flex truncate font-semibold">
{{
currentUser.displayName ||
t("profile.default_hopp_displayname")
}}
</span>
<span class="inline-flex truncate text-secondaryLight">
{{ currentUser.email }}
</span>
</div>
<hr />
<HoppSmartItem <HoppSmartItem
ref="profile" :label="t('header.download_app')"
to="/profile" :icon="IconDownload"
:icon="IconUser"
:label="t('navigation.profile')"
:shortcut="['P']"
@click="hide()"
/> />
<HoppSmartItem <HoppSmartItem
ref="settings" v-if="showInstallButton"
to="/settings" :label="t('header.install_pwa')"
:icon="IconSettings" :icon="IconPlusSquare"
:label="t('navigation.settings')" @click="installPWA()"
:shortcut="['S']"
@click="hide()"
/>
<FirebaseLogout
ref="logout"
:shortcut="['L']"
@confirm-logout="hide()"
/> />
</div> </div>
</template> </template>
</tippy> </tippy>
</span> </span>
</div> </div>
<div class="flex">
<div
v-if="currentUser === null"
class="inline-flex items-center space-x-2"
>
<HoppButtonSecondary
:icon="IconUploadCloud"
:label="t('header.save_workspace')"
class="!focus-visible:text-emerald-600 !hover:text-emerald-600 hidden h-8 border border-emerald-600/25 bg-emerald-500/10 !text-emerald-500 hover:border-emerald-600/20 hover:bg-emerald-600/20 focus-visible:border-emerald-600/20 focus-visible:bg-emerald-600/20 md:flex"
@click="invokeAction('modals.login.toggle')"
/>
<HoppButtonPrimary
:label="t('header.login')"
class="h-8"
@click="invokeAction('modals.login.toggle')"
/>
</div>
<div v-else class="inline-flex items-center space-x-2">
<TeamsMemberStack
v-if="
workspace.type === 'team' &&
selectedTeam &&
selectedTeam.teamMembers.length > 1
"
:team-members="selectedTeam.teamMembers"
show-count
class="mx-2"
@handle-click="handleTeamEdit()"
/>
<div
class="flex h-8 divide-x divide-emerald-600/25 rounded border border-emerald-600/25 bg-emerald-500/10 focus-within:divide-emerald-600/20 focus-within:border-emerald-600/20 focus-within:bg-emerald-600/20 hover:divide-emerald-600/20 hover:border-emerald-600/20 hover:bg-emerald-600/20"
>
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="t('team.invite_tooltip')"
:icon="IconUserPlus"
class="!focus-visible:text-emerald-600 !hover:text-emerald-600 !text-emerald-500"
@click="handleInvite()"
/>
<HoppButtonSecondary
v-if="
workspace.type === 'team' &&
selectedTeam &&
selectedTeam?.myRole === 'OWNER'
"
v-tippy="{ theme: 'tooltip' }"
:title="t('team.edit')"
:icon="IconSettings"
class="!focus-visible:text-emerald-600 !hover:text-emerald-600 !text-emerald-500"
@click="handleTeamEdit()"
/>
</div>
<tippy
interactive
trigger="click"
theme="popover"
:on-shown="() => accountActions.focus()"
>
<HoppButtonSecondary
v-tippy="{ theme: 'tooltip' }"
:title="t('workspace.change')"
:label="mdAndLarger ? workspaceName : ``"
:icon="workspace.type === 'personal' ? IconUser : IconUsers"
class="select-wrapper !focus-visible:text-blue-600 !hover:text-blue-600 h-8 rounded border border-blue-600/25 bg-blue-500/10 pr-8 !text-blue-500 hover:border-blue-600/20 hover:bg-blue-600/20 focus-visible:border-blue-600/20 focus-visible:bg-blue-600/20"
/>
<template #content="{ hide }">
<div
ref="accountActions"
class="flex flex-col focus:outline-none"
tabindex="0"
@keyup.escape="hide()"
@click="hide()"
>
<WorkspaceSelector />
</div>
</template>
</tippy>
<span class="px-2">
<tippy
interactive
trigger="click"
theme="popover"
:on-shown="() => tippyActions.focus()"
>
<HoppSmartPicture
v-if="currentUser.photoURL"
v-tippy="{
theme: 'tooltip',
}"
:url="currentUser.photoURL"
:alt="
currentUser.displayName ||
t('profile.default_hopp_displayname')
"
:title="
currentUser.displayName ||
currentUser.email ||
t('profile.default_hopp_displayname')
"
indicator
:indicator-styles="
network.isOnline ? 'bg-emerald-500' : 'bg-red-500'
"
/>
<HoppSmartPicture
v-else
v-tippy="{ theme: 'tooltip' }"
:title="
currentUser.displayName ||
currentUser.email ||
t('profile.default_hopp_displayname')
"
:initial="currentUser.displayName || currentUser.email"
indicator
:indicator-styles="
network.isOnline ? 'bg-emerald-500' : 'bg-red-500'
"
/>
<template #content="{ hide }">
<div
ref="tippyActions"
class="flex flex-col focus:outline-none"
tabindex="0"
@keyup.p="profile.$el.click()"
@keyup.s="settings.$el.click()"
@keyup.l="logout.$el.click()"
@keyup.escape="hide()"
>
<div class="flex flex-col px-2 text-tiny">
<span class="inline-flex truncate font-semibold">
{{
currentUser.displayName ||
t("profile.default_hopp_displayname")
}}
</span>
<span class="inline-flex truncate text-secondaryLight">
{{ currentUser.email }}
</span>
</div>
<hr />
<HoppSmartItem
ref="profile"
to="/profile"
:icon="IconUser"
:label="t('navigation.profile')"
:shortcut="['P']"
@click="hide()"
/>
<HoppSmartItem
ref="settings"
to="/settings"
:icon="IconSettings"
:label="t('navigation.settings')"
:shortcut="['S']"
@click="hide()"
/>
<FirebaseLogout
ref="logout"
:shortcut="['L']"
@confirm-logout="hide()"
/>
</div>
</template>
</tippy>
</span>
</div>
</div>
</div> </div>
</header> </header>
<AppBanner v-if="bannerContent" :banner="bannerContent" /> <AppBanner v-if="bannerContent" :banner="bannerContent" />
@@ -233,7 +289,6 @@
@invite-team="inviteTeam(editingTeamName, editingTeamID)" @invite-team="inviteTeam(editingTeamName, editingTeamID)"
@refetch-teams="refetchTeams" @refetch-teams="refetchTeams"
/> />
<HoppSmartConfirmModal <HoppSmartConfirmModal
:show="confirmRemove" :show="confirmRemove"
:title="t('confirm.remove_team')" :title="t('confirm.remove_team')"
@@ -263,6 +318,10 @@ import IconUploadCloud from "~icons/lucide/upload-cloud"
import IconUser from "~icons/lucide/user" import IconUser from "~icons/lucide/user"
import IconUserPlus from "~icons/lucide/user-plus" import IconUserPlus from "~icons/lucide/user-plus"
import IconUsers from "~icons/lucide/users" import IconUsers from "~icons/lucide/users"
import IconPlusSquare from "~icons/lucide/plus-square"
import IconArrowLeft from "~icons/lucide/arrow-left"
import IconArrowRight from "~icons/lucide/arrow-right"
import IconMenu from "~icons/lucide/align-left"
import { pipe } from "fp-ts/function" import { pipe } from "fp-ts/function"
import * as TE from "fp-ts/TaskEither" import * as TE from "fp-ts/TaskEither"
import { deleteTeam as backendDeleteTeam } from "~/helpers/backend/mutations/Team" import { deleteTeam as backendDeleteTeam } from "~/helpers/backend/mutations/Team"
@@ -271,9 +330,11 @@ import {
BannerContent, BannerContent,
BANNER_PRIORITY_HIGH, BANNER_PRIORITY_HIGH,
} from "~/services/banner.service" } from "~/services/banner.service"
import { useRouter } from "vue-router"
const t = useI18n() const t = useI18n()
const toast = useToast() const toast = useToast()
const router = useRouter()
/** /**
* Once the PWA code is initialized, this holds a method * Once the PWA code is initialized, this holds a method
@@ -445,6 +506,7 @@ const profile = ref<any | null>(null)
const settings = ref<any | null>(null) const settings = ref<any | null>(null)
const logout = ref<any | null>(null) const logout = ref<any | null>(null)
const accountActions = ref<any | null>(null) const accountActions = ref<any | null>(null)
const downloadActions = ref<any | null>(null)
defineActionHandler("modals.team.edit", handleTeamEdit) defineActionHandler("modals.team.edit", handleTeamEdit)