feat(workspaces): work(space) invader default logos (#2708)

* feat(workspaces): add default logo index

* Added WorkspaceAvatar component

* Added WorkspaceAvatar component

* Fix issues

* Fix Avatar

---------

Co-authored-by: Mike Tasset <mike.tasset@gmail.com>
This commit is contained in:
Chuck Driesler
2024-08-21 13:53:37 +01:00
committed by GitHub
parent b1f4ba1674
commit 63735eb044
27 changed files with 198 additions and 29 deletions
@@ -68,7 +68,11 @@
:active="isActive(item.to)"
>
<template #icon>
<UserAvatar :logo="item.logo" size="sm" />
<WorkspaceAvatar
:logo="item.logo"
:default-logo-index="item.defaultLogoIndex"
size="sm"
/>
</template>
</LayoutSidebarMenuGroupItem>
</NuxtLink>
@@ -172,7 +176,8 @@ const workspacesItems = computed(() =>
label: workspace.name,
id: workspace.id,
to: workspaceRoute(workspace.id),
logo: workspace.logo
logo: workspace.logo,
defaultLogoIndex: workspace.defaultLogoIndex
}))
: []
)
@@ -52,7 +52,11 @@
collapsible
>
<template #title-icon>
<UserAvatar :logo="workspaceItem.logo" size="sm" />
<WorkspaceAvatar
:logo="workspaceItem.logo"
:default-logo-index="workspaceItem.defaultLogoIndex"
size="sm"
/>
</template>
<LayoutSidebarMenuGroupItem
v-for="(workspaceMenuItem, itemKey) in workspaceMenuItems"
@@ -118,9 +122,9 @@ graphql(`
workspaces {
items {
...SettingsWorkspacesGeneralEditAvatar_Workspace
...WorkspaceAvatar_Workspace
id
name
logo
}
}
}
@@ -0,0 +1,45 @@
<template>
<div
:class="[
'text-foreground-on-primary flex shrink-0 items-center justify-center overflow-hidden rounded-full uppercase transition',
sizeClasses
]"
>
<div
class="h-full w-full bg-cover bg-center bg-no-repeat"
:style="{ backgroundImage: `url('${avatar}')` }"
/>
</div>
</template>
<script setup lang="ts">
import { graphql } from '~~/lib/common/generated/gql'
import type { MaybeNullOrUndefined } from '@speckle/shared'
import type { UserAvatarSize } from '@speckle/ui-components'
import { useAvatarSizeClasses } from '@speckle/ui-components'
import { useWorkspacesAvatar } from '~/lib/workspaces/composables/avatar'
graphql(`
fragment WorkspaceAvatar_Workspace on Workspace {
id
logo
defaultLogoIndex
}
`)
const props = withDefaults(
defineProps<{
size?: UserAvatarSize
logo?: MaybeNullOrUndefined<string>
defaultLogoIndex: number
}>(),
{
size: 'base'
}
)
const { sizeClasses } = useAvatarSizeClasses({ props: toRefs(props) })
const { defaultAvatar } = useWorkspacesAvatar(props.defaultLogoIndex)
const avatar = computed(() => (props.logo ? props.logo : defaultAvatar.value))
</script>
@@ -1,9 +1,11 @@
<template>
<div class="flex flex-col sm:flex-row justify-between sm:items-center">
<div class="flex gap-2 mb-3 mt-2">
<div class="flex items-center">
<UserAvatar :logo="workspaceInfo.logo" size="lg" />
</div>
<WorkspaceAvatar
:logo="workspaceInfo.logo"
:default-logo-index="workspaceInfo.defaultLogoIndex"
size="sm"
/>
<div class="flex flex-col">
<h1 class="text-heading-lg">{{ workspaceInfo.name }}</h1>
<div class="text-body-xs text-foreground-2">
@@ -46,6 +48,7 @@ import type { WorkspaceHeader_WorkspaceFragment } from '~~/lib/common/generated/
graphql(`
fragment WorkspaceHeader_Workspace on Workspace {
...WorkspaceAvatar_Workspace
id
role
name
@@ -89,7 +89,7 @@ const documents = {
"\n fragment ProjectsDashboardHeaderWorkspaces_User on User {\n ...WorkspaceInviteBanners_User\n }\n": types.ProjectsDashboardHeaderWorkspaces_UserFragmentDoc,
"\n fragment ProjectsInviteBanner on PendingStreamCollaborator {\n id\n invitedBy {\n ...LimitedUserAvatar\n }\n projectId\n projectName\n token\n user {\n id\n }\n }\n": types.ProjectsInviteBannerFragmentDoc,
"\n fragment ProjectsInviteBanners on User {\n projectInvites {\n ...ProjectsInviteBanner\n }\n }\n": types.ProjectsInviteBannersFragmentDoc,
"\n fragment SettingsDialog_User on User {\n workspaces {\n items {\n ...SettingsWorkspacesGeneralEditAvatar_Workspace\n id\n name\n logo\n }\n }\n }\n": types.SettingsDialog_UserFragmentDoc,
"\n fragment SettingsDialog_User on User {\n workspaces {\n items {\n ...SettingsWorkspacesGeneralEditAvatar_Workspace\n ...WorkspaceAvatar_Workspace\n id\n name\n }\n }\n }\n": types.SettingsDialog_UserFragmentDoc,
"\n fragment SettingsServerProjects_ProjectCollection on ProjectCollection {\n totalCount\n items {\n ...SettingsSharedProjects_Project\n }\n }\n": types.SettingsServerProjects_ProjectCollectionFragmentDoc,
"\n fragment SettingsSharedProjects_Project on Project {\n id\n name\n visibility\n createdAt\n updatedAt\n models {\n totalCount\n }\n versions {\n totalCount\n }\n team {\n id\n user {\n name\n id\n avatar\n }\n }\n }\n": types.SettingsSharedProjects_ProjectFragmentDoc,
"\n fragment SettingsUserEmails_User on User {\n id\n emails {\n ...SettingsUserEmailCards_UserEmail\n }\n }\n": types.SettingsUserEmails_UserFragmentDoc,
@@ -116,9 +116,10 @@ const documents = {
"\n fragment ThreadCommentAttachment on Comment {\n text {\n attachments {\n id\n fileName\n fileType\n fileSize\n }\n }\n }\n": types.ThreadCommentAttachmentFragmentDoc,
"\n fragment ViewerCommentsListItem on Comment {\n id\n rawText\n archived\n author {\n ...LimitedUserAvatar\n }\n createdAt\n viewedAt\n replies {\n totalCount\n cursor\n items {\n ...ViewerCommentsReplyItem\n }\n }\n replyAuthors(limit: 4) {\n totalCount\n items {\n ...FormUsersSelectItem\n }\n }\n resources {\n resourceId\n resourceType\n }\n }\n": types.ViewerCommentsListItemFragmentDoc,
"\n fragment ViewerModelVersionCardItem on Version {\n id\n message\n referencedObject\n sourceApplication\n createdAt\n previewUrl\n authorUser {\n ...LimitedUserAvatar\n }\n }\n": types.ViewerModelVersionCardItemFragmentDoc,
"\n fragment WorkspaceAvatar_Workspace on Workspace {\n id\n logo\n defaultLogoIndex\n }\n": types.WorkspaceAvatar_WorkspaceFragmentDoc,
"\n fragment WorkspaceInviteDialog_Workspace on Workspace {\n id\n team {\n id\n user {\n id\n }\n }\n invitedTeam(filter: $invitesFilter) {\n title\n user {\n id\n }\n }\n }\n": types.WorkspaceInviteDialog_WorkspaceFragmentDoc,
"\n fragment WorkspaceProjectList_ProjectCollection on ProjectCollection {\n totalCount\n items {\n ...ProjectDashboardItem\n }\n cursor\n }\n": types.WorkspaceProjectList_ProjectCollectionFragmentDoc,
"\n fragment WorkspaceHeader_Workspace on Workspace {\n id\n role\n name\n logo\n description\n totalProjects: projects {\n totalCount\n }\n team {\n id\n user {\n id\n name\n ...LimitedUserAvatar\n }\n }\n ...WorkspaceInviteDialog_Workspace\n }\n": types.WorkspaceHeader_WorkspaceFragmentDoc,
"\n fragment WorkspaceHeader_Workspace on Workspace {\n ...WorkspaceAvatar_Workspace\n id\n role\n name\n logo\n description\n totalProjects: projects {\n totalCount\n }\n team {\n id\n user {\n id\n name\n ...LimitedUserAvatar\n }\n }\n ...WorkspaceInviteDialog_Workspace\n }\n": types.WorkspaceHeader_WorkspaceFragmentDoc,
"\n fragment WorkspaceInviteBanner_PendingWorkspaceCollaborator on PendingWorkspaceCollaborator {\n id\n invitedBy {\n id\n ...LimitedUserAvatar\n }\n workspaceId\n workspaceName\n token\n user {\n id\n }\n ...UseWorkspaceInviteManager_PendingWorkspaceCollaborator\n }\n": types.WorkspaceInviteBanner_PendingWorkspaceCollaboratorFragmentDoc,
"\n fragment WorkspaceInviteBanners_User on User {\n workspaceInvites {\n ...WorkspaceInviteBanner_PendingWorkspaceCollaborator\n }\n }\n": types.WorkspaceInviteBanners_UserFragmentDoc,
"\n fragment WorkspaceInviteBlock_PendingWorkspaceCollaborator on PendingWorkspaceCollaborator {\n id\n workspaceId\n workspaceName\n token\n user {\n id\n name\n ...LimitedUserAvatar\n }\n title\n email\n ...UseWorkspaceInviteManager_PendingWorkspaceCollaborator\n }\n": types.WorkspaceInviteBlock_PendingWorkspaceCollaboratorFragmentDoc,
@@ -621,7 +622,7 @@ export function graphql(source: "\n fragment ProjectsInviteBanners on User {\n
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment SettingsDialog_User on User {\n workspaces {\n items {\n ...SettingsWorkspacesGeneralEditAvatar_Workspace\n id\n name\n logo\n }\n }\n }\n"): (typeof documents)["\n fragment SettingsDialog_User on User {\n workspaces {\n items {\n ...SettingsWorkspacesGeneralEditAvatar_Workspace\n id\n name\n logo\n }\n }\n }\n"];
export function graphql(source: "\n fragment SettingsDialog_User on User {\n workspaces {\n items {\n ...SettingsWorkspacesGeneralEditAvatar_Workspace\n ...WorkspaceAvatar_Workspace\n id\n name\n }\n }\n }\n"): (typeof documents)["\n fragment SettingsDialog_User on User {\n workspaces {\n items {\n ...SettingsWorkspacesGeneralEditAvatar_Workspace\n ...WorkspaceAvatar_Workspace\n id\n name\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -726,6 +727,10 @@ export function graphql(source: "\n fragment ViewerCommentsListItem on Comment
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment ViewerModelVersionCardItem on Version {\n id\n message\n referencedObject\n sourceApplication\n createdAt\n previewUrl\n authorUser {\n ...LimitedUserAvatar\n }\n }\n"): (typeof documents)["\n fragment ViewerModelVersionCardItem on Version {\n id\n message\n referencedObject\n sourceApplication\n createdAt\n previewUrl\n authorUser {\n ...LimitedUserAvatar\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment WorkspaceAvatar_Workspace on Workspace {\n id\n logo\n defaultLogoIndex\n }\n"): (typeof documents)["\n fragment WorkspaceAvatar_Workspace on Workspace {\n id\n logo\n defaultLogoIndex\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -737,7 +742,7 @@ export function graphql(source: "\n fragment WorkspaceProjectList_ProjectCollec
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function graphql(source: "\n fragment WorkspaceHeader_Workspace on Workspace {\n id\n role\n name\n logo\n description\n totalProjects: projects {\n totalCount\n }\n team {\n id\n user {\n id\n name\n ...LimitedUserAvatar\n }\n }\n ...WorkspaceInviteDialog_Workspace\n }\n"): (typeof documents)["\n fragment WorkspaceHeader_Workspace on Workspace {\n id\n role\n name\n logo\n description\n totalProjects: projects {\n totalCount\n }\n team {\n id\n user {\n id\n name\n ...LimitedUserAvatar\n }\n }\n ...WorkspaceInviteDialog_Workspace\n }\n"];
export function graphql(source: "\n fragment WorkspaceHeader_Workspace on Workspace {\n ...WorkspaceAvatar_Workspace\n id\n role\n name\n logo\n description\n totalProjects: projects {\n totalCount\n }\n team {\n id\n user {\n id\n name\n ...LimitedUserAvatar\n }\n }\n ...WorkspaceInviteDialog_Workspace\n }\n"): (typeof documents)["\n fragment WorkspaceHeader_Workspace on Workspace {\n ...WorkspaceAvatar_Workspace\n id\n role\n name\n logo\n description\n totalProjects: projects {\n totalCount\n }\n team {\n id\n user {\n id\n name\n ...LimitedUserAvatar\n }\n }\n ...WorkspaceInviteDialog_Workspace\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
File diff suppressed because one or more lines are too long
@@ -0,0 +1,13 @@
export const useWorkspacesAvatar = (avatarIndex: number) => {
const count = ref(4)
const defaultAvatar = computed(() => {
const index = avatarIndex >= 0 && avatarIndex <= count.value ? avatarIndex : 0
return `/images/workspace/avatars/avatar_${index}.svg`
})
return {
count,
defaultAvatar
}
}
@@ -0,0 +1,16 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 8H8V12H12V8Z" fill="#FBBF24"/>
<path d="M12 12H8V18H12V12Z" fill="#FBBF24"/>
<path d="M12 28H8V32H12V28Z" fill="#FBBF24"/>
<path d="M18 22H12V28H18V22Z" fill="#FBBF24"/>
<path d="M18 28H12V32H18V28Z" fill="#FBBF24"/>
<path d="M22 12H18V18H22V12Z" fill="#FBBF24"/>
<path d="M22 18H18V22H22V18Z" fill="#FBBF24"/>
<path d="M22 22H18V28H22V22Z" fill="#FBBF24"/>
<path d="M22 28H18V32H22V28Z" fill="#FBBF24"/>
<path d="M32 8H28V12H32V8Z" fill="#FBBF24"/>
<path d="M32 12H28V18H32V12Z" fill="#FBBF24"/>
<path d="M32 28H28V32H32V28Z" fill="#FBBF24"/>
<path d="M28 22H22V28H28V22Z" fill="#FBBF24"/>
<path d="M28 28H22V32H28V28Z" fill="#FBBF24"/>
</svg>

After

Width:  |  Height:  |  Size: 754 B

@@ -0,0 +1,13 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 8H8V14H12V8Z" fill="#1CBA80"/>
<path d="M12 22H8V28H12V22Z" fill="#1CBA80"/>
<path d="M12 28H8V32H12V28Z" fill="#1CBA80"/>
<path d="M18 18H12V22H18V18Z" fill="#1CBA80"/>
<path d="M22 8H18V14H22V8Z" fill="#1CBA80"/>
<path d="M22 18H18V22H22V18Z" fill="#1CBA80"/>
<path d="M24 26H16V32H24V26Z" fill="#1CBA80"/>
<path d="M32 8H28V14H32V8Z" fill="#1CBA80"/>
<path d="M32 22H28V28H32V22Z" fill="#1CBA80"/>
<path d="M32 28H28V32H32V28Z" fill="#1CBA80"/>
<path d="M28 18H22V22H28V18Z" fill="#1CBA80"/>
</svg>

After

Width:  |  Height:  |  Size: 611 B

@@ -0,0 +1,8 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 8H8V14H12V8Z" fill="#4B40C9"/>
<path d="M22 8H18V24H22V8Z" fill="#4B40C9"/>
<path d="M26 24H14V28H26V24Z" fill="#4B40C9"/>
<path d="M32 14H8V18H32V14Z" fill="#4B40C9"/>
<path d="M32 28H8V32H32V28Z" fill="#4B40C9"/>
<path d="M32 8H28V14H32V8Z" fill="#4B40C9"/>
</svg>

After

Width:  |  Height:  |  Size: 376 B

@@ -0,0 +1,8 @@
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 16H8V22H12V16Z" fill="#C45959"/>
<path d="M26 28H14V34H26V28Z" fill="#C45959"/>
<path d="M18 6H22V10H18V6Z" fill="#C45959"/>
<path d="M18 18H22V22H18V18Z" fill="#C45959"/>
<path d="M32 22H8V28H32V22Z" fill="#C45959"/>
<path d="M32 16H28V22H32V16Z" fill="#C45959"/>
</svg>

After

Width:  |  Height:  |  Size: 381 B

@@ -16,6 +16,7 @@ extend type Query {
input WorkspaceCreateInput {
name: String!
description: String
defaultLogoIndex: Int
}
input WorkspaceUpdateInput {
@@ -26,6 +27,7 @@ input WorkspaceUpdateInput {
Logo image as base64-encoded string
"""
logo: String
defaultLogoIndex: Int
}
input WorkspaceRoleUpdateInput {
@@ -162,6 +164,10 @@ type Workspace {
"""
logo: String
"""
Selected fallback when `logo` not set
"""
defaultLogoIndex: Int!
"""
Active user's role for this workspace. `null` if request is not authenticated, or the workspace is not explicitly shared with you.
"""
role: String
@@ -3820,6 +3820,8 @@ export type WebhookUpdateInput = {
export type Workspace = {
__typename?: 'Workspace';
createdAt: Scalars['DateTime']['output'];
/** Selected fallback when `logo` not set */
defaultLogoIndex: Scalars['Int']['output'];
description?: Maybe<Scalars['String']['output']>;
id: Scalars['ID']['output'];
/** Only available to workspace owners */
@@ -3866,6 +3868,7 @@ export type WorkspaceCollection = {
};
export type WorkspaceCreateInput = {
defaultLogoIndex?: InputMaybe<Scalars['Int']['input']>;
description?: InputMaybe<Scalars['String']['input']>;
name: Scalars['String']['input'];
};
@@ -4012,6 +4015,7 @@ export type WorkspaceTeamFilter = {
};
export type WorkspaceUpdateInput = {
defaultLogoIndex?: InputMaybe<Scalars['Int']['input']>;
description?: InputMaybe<Scalars['String']['input']>;
id: Scalars['String']['input'];
/** Logo image as base64-encoded string */
@@ -5878,6 +5882,7 @@ export type WebhookEventCollectionResolvers<ContextType = GraphQLContext, Parent
export type WorkspaceResolvers<ContextType = GraphQLContext, ParentType extends ResolversParentTypes['Workspace'] = ResolversParentTypes['Workspace']> = {
createdAt?: Resolver<ResolversTypes['DateTime'], ParentType, ContextType>;
defaultLogoIndex?: Resolver<ResolversTypes['Int'], ParentType, ContextType>;
description?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>;
invitedTeam?: Resolver<Maybe<Array<ResolversTypes['PendingWorkspaceCollaborator']>>, ParentType, ContextType, Partial<WorkspaceInvitedTeamArgs>>;
@@ -3809,6 +3809,8 @@ export type WebhookUpdateInput = {
export type Workspace = {
__typename?: 'Workspace';
createdAt: Scalars['DateTime']['output'];
/** Selected fallback when `logo` not set */
defaultLogoIndex: Scalars['Int']['output'];
description?: Maybe<Scalars['String']['output']>;
id: Scalars['ID']['output'];
/** Only available to workspace owners */
@@ -3855,6 +3857,7 @@ export type WorkspaceCollection = {
};
export type WorkspaceCreateInput = {
defaultLogoIndex?: InputMaybe<Scalars['Int']['input']>;
description?: InputMaybe<Scalars['String']['input']>;
name: Scalars['String']['input'];
};
@@ -4001,6 +4004,7 @@ export type WorkspaceTeamFilter = {
};
export type WorkspaceUpdateInput = {
defaultLogoIndex?: InputMaybe<Scalars['Int']['input']>;
description?: InputMaybe<Scalars['String']['input']>;
id: Scalars['String']['input'];
/** Logo image as base64-encoded string */
@@ -10,6 +10,7 @@ const createFakeWorkspace = (): Workspace => {
id: cryptoRandomString({ length: 10 }),
description: cryptoRandomString({ length: 10 }),
logo: null,
defaultLogoIndex: 0,
name: cryptoRandomString({ length: 10 }),
updatedAt: new Date(),
createdAt: new Date()
@@ -220,7 +220,7 @@ export = FF_WORKSPACES_MODULE_ENABLED
},
WorkspaceMutations: {
create: async (_parent, args, context) => {
const { name, description } = args.input
const { name, description, defaultLogoIndex } = args.input
const createWorkspace = createWorkspaceFactory({
upsertWorkspace: upsertWorkspaceFactory({ db }),
@@ -233,7 +233,8 @@ export = FF_WORKSPACES_MODULE_ENABLED
workspaceInput: {
name,
description: description || null,
logo: null
logo: null,
defaultLogoIndex: defaultLogoIndex || 0
},
userResourceAccessLimits: context.resourceAccessRules
})
@@ -6,7 +6,8 @@ export const Workspaces = buildTableHelper('workspaces', [
'description',
'createdAt',
'updatedAt',
'logo'
'logo',
'defaultLogoIndex'
])
export const WorkspaceAcl = buildTableHelper('workspace_acl', [
@@ -92,7 +92,7 @@ export const upsertWorkspaceFactory =
.workspaces(db)
.insert(workspace)
.onConflict('id')
.merge(['description', 'logo', 'name', 'updatedAt'])
.merge(['description', 'logo', 'defaultLogoIndex', 'name', 'updatedAt'])
}
export const deleteWorkspaceFactory =
@@ -51,6 +51,7 @@ type WorkspaceCreateArgs = {
name: string
description: string | null
logo: string | null
defaultLogoIndex: number
}
userResourceAccessLimits: MaybeNullOrUndefined<TokenResourceIdentifier[]>
}
@@ -108,6 +109,7 @@ type WorkspaceUpdateArgs = {
name?: string | null
description?: string | null
logo?: string | null
defaultLogoIndex?: number | null
}
}
@@ -61,7 +61,8 @@ export const createTestWorkspace = async (
workspaceInput: {
name: workspace.name,
description: workspace.description || null,
logo: workspace.logo || null
logo: workspace.logo || null,
defaultLogoIndex: 0
},
userResourceAccessLimits: null
})
@@ -51,7 +51,8 @@ const createAndStoreTestWorkspace = async (): Promise<Workspace> => {
createdAt: new Date(),
updatedAt: new Date(),
description: null,
logo: null
logo: null,
defaultLogoIndex: 0
}
await upsertWorkspace({ workspace })
@@ -61,7 +61,8 @@ const getCreateWorkspaceInput = () => {
workspaceInput: {
description: 'foobar',
logo: null,
name: cryptoRandomString({ length: 6 })
name: cryptoRandomString({ length: 6 }),
defaultLogoIndex: 0
}
}
}
@@ -7,6 +7,7 @@ export type Workspace = {
createdAt: Date
updatedAt: Date
logo: string | null
defaultLogoIndex: number
}
export type WorkspaceWithOptionalRole = Workspace & { role?: WorkspaceRoles }
@@ -0,0 +1,13 @@
import { Knex } from 'knex'
export async function up(knex: Knex): Promise<void> {
await knex.schema.alterTable('workspaces', (table) => {
table.integer('defaultLogoIndex').defaultTo(0).notNullable()
})
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.alterTable('workspaces', (table) => {
table.dropColumn('defaultLogoIndex')
})
}
@@ -3810,6 +3810,8 @@ export type WebhookUpdateInput = {
export type Workspace = {
__typename?: 'Workspace';
createdAt: Scalars['DateTime']['output'];
/** Selected fallback when `logo` not set */
defaultLogoIndex: Scalars['Int']['output'];
description?: Maybe<Scalars['String']['output']>;
id: Scalars['ID']['output'];
/** Only available to workspace owners */
@@ -3856,6 +3858,7 @@ export type WorkspaceCollection = {
};
export type WorkspaceCreateInput = {
defaultLogoIndex?: InputMaybe<Scalars['Int']['input']>;
description?: InputMaybe<Scalars['String']['input']>;
name: Scalars['String']['input'];
};
@@ -4002,6 +4005,7 @@ export type WorkspaceTeamFilter = {
};
export type WorkspaceUpdateInput = {
defaultLogoIndex?: InputMaybe<Scalars['Int']['input']>;
description?: InputMaybe<Scalars['String']['input']>;
id: Scalars['String']['input'];
/** Logo image as base64-encoded string */
@@ -11,9 +11,9 @@
>
<slot>
<div
v-if="user?.avatar || logo"
v-if="user?.avatar"
class="h-full w-full bg-cover bg-center bg-no-repeat"
:style="{ backgroundImage: `url('${user ? user.avatar : logo}')` }"
:style="{ backgroundImage: `url('${user.avatar}')` }"
/>
<div
v-else-if="initials"
@@ -41,7 +41,6 @@ const props = withDefaults(
active?: boolean
noBorder?: boolean
noBg?: boolean
logo?: MaybeNullOrUndefined<string>
}>(),
{
size: 'base',
+2
View File
@@ -96,6 +96,8 @@ import type { AvatarUser, AvatarUserWithId } from '~~/src/composables/user/avata
import { useDebouncedTextInput } from '~~/src/composables/form/textInput'
export { vKeyboardClickable } from '~~/src/directives/accessibility'
export { useAvatarSizeClasses } from '~~/src/composables/user/avatar'
export type { UserAvatarSize } from '~~/src/composables/user/avatar'
export {
CommonLoadingIcon,