Skip to content

Commit

Permalink
feat(chat): add permission card avatars
Browse files Browse the repository at this point in the history
  • Loading branch information
Mati365 committed Jan 12, 2025
1 parent 47087d8 commit 7471db1
Show file tree
Hide file tree
Showing 14 changed files with 135 additions and 33 deletions.
7 changes: 5 additions & 2 deletions apps/chat/src/i18n/packs/i18n-lang-en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -482,9 +482,12 @@ export const I18N_PACK_EN = deepmerge(I18N_FORWARDED_EN_PACK, {
permissions: {
share: 'Share',
accessLevels: I18N_ACCESS_LEVELS_EN,
card: {
sharedWith: 'Shared with',
},
status: {
publicTooltip: 'Everyone in organization can see this project',
privateTooltip: 'Only shared users and groups can see this project',
publicTooltip: 'Everyone in organization can see this',
privateTooltip: 'Only shared users and groups can see this',
},
modal: {
title: 'Share resource',
Expand Down
7 changes: 5 additions & 2 deletions apps/chat/src/i18n/packs/i18n-lang-pl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -484,9 +484,12 @@ export const I18N_PACK_PL: I18nLangPack = deepmerge(I18N_FORWARDED_PL_PACK, {
permissions: {
share: 'Udostępnij',
accessLevels: I18N_ACCESS_LEVELS_PL,
card: {
sharedWith: 'Udostępniono dla',
},
status: {
publicTooltip: 'Wszyscy w organizacji widzą ten projekt',
privateTooltip: 'Tylko udostępnieni użytkownicy i grupy widzą ten projekt',
publicTooltip: 'Wszyscy w organizacji to widzą',
privateTooltip: 'Tylko udostępnieni użytkownicy i grupy to widzą',
},
modal: {
title: 'Udostępnij',
Expand Down
17 changes: 17 additions & 0 deletions apps/chat/src/modules/permissions/card/card-record-permissions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { isSdkPublicPermissions, type SdkPermissionT } from '@llm/sdk';

import { CardRecordPublic } from './card-record-public';
import { CardRecordSharedWith } from './card-record-shared-with';

type Props = {
permissions: SdkPermissionT[];
className?: string;
};

export function CardRecordPermissions({ permissions, ...props }: Props) {
if (isSdkPublicPermissions(permissions)) {
return <CardRecordPublic permissions={permissions} {...props} />;
}

return <CardRecordSharedWith permissions={permissions} {...props} />;
}
26 changes: 26 additions & 0 deletions apps/chat/src/modules/permissions/card/card-record-public.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import clsx from 'clsx';
import { GlobeIcon } from 'lucide-react';

import type { SdkPermissionT } from '@llm/sdk';

import { useI18n } from '~/i18n';

type Props = {
permissions: SdkPermissionT[];
className?: string;
};

export function CardRecordPublic({ className }: Props) {
const t = useI18n().pack.permissions.status;

return (
<div className={clsx('flex items-center gap-2', className)}>
<div className="flex items-center gap-1.5 text-gray-500">
<GlobeIcon size={16} />
<span className="text-sm">
{t.publicTooltip}
</span>
</div>
</div>
);
}
31 changes: 31 additions & 0 deletions apps/chat/src/modules/permissions/card/card-record-shared-with.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import clsx from 'clsx';
import { UsersIcon } from 'lucide-react';

import type { SdkPermissionT } from '@llm/sdk';

import { useI18n } from '~/i18n';

import { PermissionAvatarsList } from '../list/permission-avatars-list';

type Props = {
permissions: SdkPermissionT[];
className?: string;
};

export function CardRecordSharedWith({ permissions, className }: Props) {
const t = useI18n().pack.permissions.card;

return (
<div className={clsx('flex items-center gap-2', className)}>
<div className="flex items-center gap-1.5 text-gray-500">
<UsersIcon size={16} />
<span className="text-sm">
{t.sharedWith}
:
</span>
</div>

<PermissionAvatarsList permissions={permissions} size="xs" />
</div>
);
}
3 changes: 3 additions & 0 deletions apps/chat/src/modules/permissions/card/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './card-record-permissions';
export * from './card-record-public';
export * from './card-record-shared-with';
1 change: 1 addition & 0 deletions apps/chat/src/modules/permissions/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './card';
export * from './controls';
export * from './list';
export * from './share-resource';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import clsx from 'clsx';

import type { SdkPermissionT } from '@llm/sdk';
import type { ColorizedAvatarSize } from '@llm/ui';

import { PermissionGroupAvatar } from './permission-group-avatar';
import { PermissionUserAvatar } from './permission-user-avatar';

type Props = {
permissions: SdkPermissionT[];
className?: string;
size?: ColorizedAvatarSize;
};

export function PermissionAvatarsList({ permissions, className }: Props) {
export function PermissionAvatarsList({ permissions, className, size }: Props) {
return (
<div className={clsx('flex -space-x-2', className)}>
{permissions.map((permission) => {
Expand All @@ -23,6 +25,7 @@ export function PermissionAvatarsList({ permissions, className }: Props) {
<PermissionUserAvatar
user={permission.target.user}
accessLevel={permission.accessLevel}
size={size}
/>
</div>
);
Expand All @@ -33,6 +36,7 @@ export function PermissionAvatarsList({ permissions, className }: Props) {
<PermissionGroupAvatar
group={permission.target.group}
accessLevel={permission.accessLevel}
size={size}
/>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@ import { Users2 } from 'lucide-react';

import type { SdkTableRowWithIdNameT } from '@llm/sdk';

import { Avatar, Tooltip } from '@llm/ui';
import { Avatar, type AvatarSize, Tooltip } from '@llm/ui';

type Props = {
group: SdkTableRowWithIdNameT;
accessLevel: string;
size?: AvatarSize;
};

export function PermissionGroupAvatar({ group, accessLevel }: Props) {
export function PermissionGroupAvatar({ group, accessLevel, size = 'sm' }: Props) {
return (
<Tooltip content={`${group.name} (${accessLevel})`}>
<Avatar
size="sm"
size={size}
name={group.name}
fallback={<Users2 size={24} />}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import type { SdkPermissionAccessLevelT, SdkUserListItemT } from '@llm/sdk';

import { ColorizedAvatar, Tooltip } from '@llm/ui';
import { ColorizedAvatar, type ColorizedAvatarSize, Tooltip } from '@llm/ui';
import { useI18n } from '~/i18n';

type Props = {
user: SdkUserListItemT;
accessLevel: SdkPermissionAccessLevelT;
size?: ColorizedAvatarSize;
};

export function PermissionUserAvatar({ user, accessLevel }: Props) {
export function PermissionUserAvatar({ user, accessLevel, size = 'sm' }: Props) {
const { accessLevels } = useI18n().pack.permissions;

return (
Expand All @@ -17,7 +18,7 @@ export function PermissionUserAvatar({ user, accessLevel }: Props) {
<ColorizedAvatar
id={user.id}
name={user.name}
size="sm"
size={size}
className="shadow-sm"
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import clsx from 'clsx';
import { EyeIcon, LockIcon } from 'lucide-react';
import { EyeIcon, UsersIcon } from 'lucide-react';

import { isSdkPublicPermissions, type SdkPermissionT } from '@llm/sdk';
import { Tooltip } from '@llm/ui';
Expand All @@ -22,7 +22,7 @@ export function PermissionsStatusIcon({ permissions, className }: Props) {
{(
isPublic
? <EyeIcon size={14} />
: <LockIcon size={14} />
: <UsersIcon size={14} />
)}
</Tooltip>
);
Expand Down
34 changes: 21 additions & 13 deletions apps/chat/src/modules/projects/grid/project-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
CardTitle,
useArchiveWithNotifications,
} from '@llm/ui';
import { CardRecordPermissions } from '~/modules/permissions/card';
import { useSitemap } from '~/routes';

import { useProjectUpdateModal } from '../form';
Expand Down Expand Up @@ -48,21 +49,28 @@ export function ProjectCard({ project, onAfterEdit, onAfterArchive }: ProjectCar
{project.name}
</CardTitle>

{description
? (
<CardDescription>
{description}
</CardDescription>
)
: <div className="flex-1 my-2" />}
<div className="flex flex-col flex-1 space-y-4">
{project.permissions && (
<CardRecordPermissions
permissions={project.permissions.current}
className="text-sm"
/>
)}

{description && (
<CardDescription className="flex-1">
{description}
</CardDescription>
)}

<CardFooter>
<div className="text-muted-foreground text-xs">
{formatDate(project.updatedAt)}
</div>
<CardFooter>
<div className="text-muted-foreground text-xs">
{formatDate(project.updatedAt)}
</div>

<CardOpenButton href={sitemap.projects.show.generate({ pathParams: { id: project.id } })} />
</CardFooter>
<CardOpenButton href={sitemap.projects.show.generate({ pathParams: { id: project.id } })} />
</CardFooter>
</div>

{!project.archived && (
<CardActions>
Expand Down
3 changes: 2 additions & 1 deletion packages/ui/src/components/avatar/avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import type { ReactNode } from 'react';

import clsx from 'clsx';

type AvatarSize = 'sm' | 'md' | 'lg';
export type AvatarSize = 'sm' | 'md' | 'lg' | 'xs';

const sizeClasses: Record<AvatarSize, string> = {
xs: 'w-6 h-6',
sm: 'w-8 h-8',
md: 'w-10 h-10',
lg: 'w-12 h-12',
Expand Down
15 changes: 9 additions & 6 deletions packages/ui/src/components/avatar/colorized-avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import { useMemo } from 'react';

import type { SdkTableRowIdT } from '@llm/sdk';

const sizeClasses = {
const SIZE_CLASSES = {
xs: 'h-6 w-6 text-xs',
sm: 'h-8 w-8 text-sm',
md: 'h-10 w-10 text-base',
lg: 'h-12 w-12 text-lg',
} as const;

const colors = [
const COLORS = [
'bg-red-100 text-red-800 border-2 border-red-200',
'bg-blue-100 text-blue-800 border-2 border-blue-200',
'bg-green-100 text-green-800 border-2 border-green-200',
Expand All @@ -22,11 +23,13 @@ const colors = [
let COLOR_COUNTER = 0;
const COLOR_CACHE = new Map<string, string>();

export type ColorizedAvatarSize = keyof typeof SIZE_CLASSES;

type Props = {
className?: string;
id: SdkTableRowIdT;
name: string;
size?: keyof typeof sizeClasses;
size?: ColorizedAvatarSize;
};

export function ColorizedAvatar({ id, className, name, size = 'md' }: Props) {
Expand All @@ -35,9 +38,9 @@ export function ColorizedAvatar({ id, className, name, size = 'md' }: Props) {
const cacheKey = `${name}#${id}`;

if (!COLOR_CACHE.has(cacheKey)) {
const colorIndex = COLOR_COUNTER % colors.length;
const colorIndex = COLOR_COUNTER % COLORS.length;

COLOR_CACHE.set(cacheKey, colors[colorIndex]);
COLOR_CACHE.set(cacheKey, COLORS[colorIndex]);
COLOR_COUNTER++;
}

Expand All @@ -48,7 +51,7 @@ export function ColorizedAvatar({ id, className, name, size = 'md' }: Props) {
<div
className={clsx(
'inline-flex justify-center items-center rounded-full font-semibold select-none',
sizeClasses[size],
SIZE_CLASSES[size],
colorClass,
className,
)}
Expand Down

0 comments on commit 7471db1

Please sign in to comment.