Skip to content

Commit 814e9b2

Browse files
committed
refactor: some ui
1 parent d501114 commit 814e9b2

5 files changed

Lines changed: 149 additions & 168 deletions

File tree

apps/website/src/app/dashboard/[id]/ama/page.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,9 @@ export default async function AMAPage({ params }: PageProps<'/dashboard/[id]/ama
77

88
return (
99
<div className="space-y-8">
10-
<div className="space-y-4">
10+
<div className="flex flex-col gap-4">
1111
<DashboardCrumbs segments={[{ label: 'AMA' }]} />
12-
<Heading subtitle="Configure AMA bot for your server" title="AMA Settings" />
13-
</div>
14-
15-
<div className="flex flex-col gap-3">
12+
<Heading subtitle="Configure AMA for your server" title="AMA Settings" />
1613
<Link
1714
className="flex items-center gap-4 rounded-lg border-[1px] border-on-secondary bg-card p-4 hover:bg-on-tertiary dark:border-on-secondary-dark dark:bg-card-dark dark:hover:bg-on-tertiary-dark"
1815
href={`/dashboard/${id}/ama/amas`}

apps/website/src/app/dashboard/[id]/page.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,8 @@ export default function GuildPage() {
2727

2828
return (
2929
<div className="space-y-8">
30-
<DashboardCrumbs segments={[]} />
31-
32-
<GuildCard data={guild} variant="detailed" />
33-
3430
<div className="space-y-4">
31+
<DashboardCrumbs segments={[]} />
3532
<Heading subtitle="Configure the bots installed in your server" title="Bots" />
3633
<div className="flex flex-col gap-3">
3734
{BOTS.map((bot, index) => {

apps/website/src/app/dashboard/_components/GuildCard.tsx

Lines changed: 35 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -6,99 +6,57 @@ import { cn } from '@/utils/util';
66

77
interface GuildCardProps {
88
readonly data: MeGuild;
9-
readonly variant?: 'compact' | 'detailed';
109
}
1110

12-
export default function GuildCard({ data, variant = 'compact' }: GuildCardProps) {
11+
export default function GuildCard({ data }: GuildCardProps) {
1312
const hasBots = data.bots.length > 0;
1413
const url = hasBots ? `/dashboard/${data.id}` : undefined;
15-
const isDetailed = variant === 'detailed';
1614

1715
return (
1816
<div
1917
className={cn(
20-
'flex rounded-lg border-[1px] border-on-secondary p-4 dark:border-on-secondary-dark',
18+
'flex rounded-lg border-[1px] border-on-secondary p-4 dark:border-on-secondary-dark h-36 w-[80vw] flex-col gap-3 md:w-52',
2119
hasBots ? 'bg-card dark:bg-card-dark' : 'group',
22-
isDetailed ? 'w-full flex-row gap-3' : 'h-36 w-[80vw] flex-col gap-3 md:w-52',
2320
)}
2421
>
25-
{isDetailed ? (
26-
<>
27-
<div className="flex flex-row gap-4">
28-
<GuildIcon data={data} disableLink hasBots={hasBots} />
29-
<div className="flex flex-col gap-1">
30-
<p className="content-center w-full overflow-hidden overflow-ellipsis whitespace-nowrap text-lg font-medium text-primary dark:text-primary-dark">
31-
{data.name}
32-
</p>
33-
{hasBots ? (
34-
<ul className="flex flex-row gap-1">
35-
{data.bots.includes('AMA') && (
36-
<li>
37-
<SvgAMA />
38-
</li>
39-
)}
40-
</ul>
41-
) : (
42-
<p className="text-base text-secondary dark:text-secondary-dark">No bots active</p>
43-
)}
44-
</div>
45-
</div>
22+
<GuildIcon data={data} hasBots={hasBots} />
23+
<div className="flex flex-col gap-1">
24+
<p className="w-full overflow-hidden overflow-ellipsis whitespace-nowrap text-lg font-medium text-primary group-hover:hidden dark:text-primary-dark">
25+
{url ? (
26+
<Link href={url} prefetch>
27+
{data.name}
28+
</Link>
29+
) : (
30+
data.name
31+
)}
32+
</p>
4633

47-
<div className="ml-auto flex flex-col gap-1 text-right">
48-
{data.approximate_member_count !== undefined && (
49-
<p className="text-base text-secondary dark:text-secondary-dark">
50-
{data.approximate_member_count.toLocaleString()} member(s)
51-
</p>
52-
)}
53-
{data.approximate_presence_count !== undefined && (
54-
<p className="text-base text-secondary dark:text-secondary-dark">
55-
{data.approximate_presence_count?.toLocaleString() ?? '0'} online
56-
</p>
34+
{hasBots ? (
35+
<ul className="flex flex-row gap-1">
36+
{data.bots.includes('AMA') && (
37+
<li>
38+
<SvgAMA />
39+
</li>
5740
)}
58-
</div>
59-
</>
60-
) : (
61-
<>
62-
<GuildIcon data={data} hasBots={hasBots} />
63-
<div className="flex flex-col gap-1">
64-
<p className="w-full overflow-hidden overflow-ellipsis whitespace-nowrap text-lg font-medium text-primary group-hover:hidden dark:text-primary-dark">
65-
{url ? (
66-
<Link href={url} prefetch>
67-
{data.name}
68-
</Link>
69-
) : (
70-
data.name
71-
)}
41+
</ul>
42+
) : (
43+
<>
44+
<p className="text-lg font-normal text-secondary group-hover:hidden dark:text-secondary-dark">
45+
Not invited
7246
</p>
73-
74-
{hasBots ? (
75-
<ul className="flex flex-row gap-1">
76-
{data.bots.includes('AMA') && (
77-
<li>
47+
<div className="hidden flex-col gap-1 group-hover:flex">
48+
<p className="text-lg font-medium text-primary dark:text-primary-dark">Invite a bot:</p>
49+
<ul className="flex flex-row gap-3">
50+
<li>
51+
<Link href="/invites/ama">
7852
<SvgAMA />
79-
</li>
80-
)}
53+
</Link>
54+
</li>
8155
</ul>
82-
) : (
83-
<>
84-
<p className="text-lg font-normal text-secondary group-hover:hidden dark:text-secondary-dark">
85-
Not invited
86-
</p>
87-
<div className="hidden flex-col gap-1 group-hover:flex">
88-
<p className="text-lg font-medium text-primary dark:text-primary-dark">Invite a bot:</p>
89-
<ul className="flex flex-row gap-3">
90-
<li>
91-
<Link href="/invites/ama">
92-
<SvgAMA />
93-
</Link>
94-
</li>
95-
</ul>
96-
</div>
97-
</>
98-
)}
99-
</div>
100-
</>
101-
)}
56+
</div>
57+
</>
58+
)}
59+
</div>
10260
</div>
10361
);
10462
}

apps/website/src/components/common/Breadcrumb.tsx

Lines changed: 44 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,48 @@ interface BreadcrumbProps {
2121
readonly segments: BreadcrumbSegment[];
2222
}
2323

24+
export function getBreadcrumbTextStyles(isLast: boolean, highlight?: boolean) {
25+
return cn(
26+
isLast ? 'text-primary dark:text-primary-dark font-medium' : 'text-secondary dark:text-secondary-dark',
27+
highlight && 'italic',
28+
);
29+
}
30+
31+
interface BreadcrumbLabelProps {
32+
readonly highlight?: boolean;
33+
readonly href?: string | undefined;
34+
readonly icon?: React.ReactNode;
35+
readonly isLast: boolean;
36+
readonly label: string;
37+
}
38+
39+
function BreadcrumbLabel({ icon, label, href, isLast, highlight }: BreadcrumbLabelProps) {
40+
const content = (
41+
<>
42+
{icon}
43+
{label}
44+
</>
45+
);
46+
47+
if (href) {
48+
return (
49+
<Link
50+
className={cn(
51+
'flex items-center gap-2 hover:text-primary dark:hover:text-primary-dark',
52+
getBreadcrumbTextStyles(isLast, highlight),
53+
isLast && 'pointer-events-none',
54+
)}
55+
href={href}
56+
prefetch
57+
>
58+
{content}
59+
</Link>
60+
);
61+
}
62+
63+
return <span className={cn('flex items-center gap-2', getBreadcrumbTextStyles(isLast, highlight))}>{content}</span>;
64+
}
65+
2466
export function Breadcrumb({ segments }: BreadcrumbProps) {
2567
return (
2668
<nav className="flex items-center gap-2 text-lg">
@@ -31,41 +73,9 @@ export function Breadcrumb({ segments }: BreadcrumbProps) {
3173
return (
3274
<div className="flex items-center gap-2" key={`${segment.label}-${index}`}>
3375
{hasOptions ? (
34-
<BreadcrumbDropdown
35-
highlight={segment.highlight}
36-
icon={segment.icon}
37-
isLast={isLast}
38-
label={segment.label}
39-
options={segment.options}
40-
/>
41-
) : segment.href ? (
42-
<Link
43-
className={cn(
44-
'flex items-center gap-2 hover:text-primary dark:hover:text-primary-dark',
45-
isLast
46-
? 'text-primary dark:text-primary-dark font-medium'
47-
: 'text-secondary dark:text-secondary-dark',
48-
isLast && 'pointer-events-none',
49-
segment.highlight && 'italic',
50-
)}
51-
href={segment.href}
52-
prefetch
53-
>
54-
{segment.icon}
55-
{segment.label}
56-
</Link>
76+
<BreadcrumbDropdown {...segment} isLast={isLast} options={segment.options} />
5777
) : (
58-
<span
59-
className={cn(
60-
isLast
61-
? 'text-primary dark:text-primary-dark font-medium'
62-
: 'text-secondary dark:text-secondary-dark',
63-
segment.highlight && 'italic',
64-
)}
65-
>
66-
{segment.icon}
67-
{segment.label}
68-
</span>
78+
<BreadcrumbLabel {...segment} isLast={isLast} />
6979
)}
7080

7181
{!isLast && <span className="text-secondary dark:text-secondary-dark">/</span>}

apps/website/src/components/common/BreadcrumbDropdown.tsx

Lines changed: 67 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,71 +4,90 @@ import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
44
import Image from 'next/image';
55
import Link from 'next/link';
66
import type { BreadcrumbOption } from './Breadcrumb';
7+
import { getBreadcrumbTextStyles } from './Breadcrumb';
78
import { Button } from './Button';
89
import { SvgChevronDown } from '@/components/icons/SvgChevronDown';
910
import { cn, getGuildAcronym } from '@/utils/util';
1011

1112
interface BreadcrumbDropdownProps {
1213
readonly highlight?: boolean | undefined;
14+
readonly href?: string | undefined;
1315
readonly icon?: React.ReactNode;
1416
readonly isLast: boolean;
1517
readonly label: string;
1618
readonly options: readonly BreadcrumbOption[];
1719
}
1820

19-
export function BreadcrumbDropdown({ label, icon, options, isLast, highlight }: BreadcrumbDropdownProps) {
21+
export function BreadcrumbDropdown({ label, icon, href, options, isLast, highlight }: BreadcrumbDropdownProps) {
22+
const textStyles = getBreadcrumbTextStyles(isLast, highlight);
23+
const content = (
24+
<>
25+
{icon}
26+
{label}
27+
</>
28+
);
29+
2030
return (
21-
<DropdownMenu.Root>
22-
<DropdownMenu.Trigger asChild>
23-
<Button
31+
<div className="flex items-center gap-1">
32+
{href ? (
33+
<Link
2434
className={cn(
25-
'flex items-center gap-1 hover:text-primary dark:hover:text-primary-dark px-0',
26-
isLast ? 'text-primary dark:text-primary-dark font-medium' : 'text-secondary dark:text-secondary-dark',
27-
highlight && 'italic',
35+
'flex items-center gap-2 hover:text-primary dark:hover:text-primary-dark',
36+
textStyles,
37+
isLast && 'pointer-events-none',
2838
)}
39+
href={href}
40+
prefetch
2941
>
30-
{icon}
31-
{label}
32-
<SvgChevronDown />
33-
</Button>
34-
</DropdownMenu.Trigger>
42+
{content}
43+
</Link>
44+
) : (
45+
<span className={cn('flex items-center gap-2', textStyles)}>{content}</span>
46+
)}
47+
<DropdownMenu.Root>
48+
<DropdownMenu.Trigger asChild>
49+
<Button className={cn('flex items-center hover:text-primary dark:hover:text-primary-dark px-0', textStyles)}>
50+
<SvgChevronDown />
51+
</Button>
52+
</DropdownMenu.Trigger>
3553

36-
<DropdownMenu.Portal>
37-
<DropdownMenu.Content
38-
className="min-w-[220px] bg-card dark:bg-card-dark border border-on-secondary dark:border-on-secondary-dark rounded-lg p-1 shadow-lg z-50"
39-
sideOffset={5}
40-
>
41-
{options.map((option, optionIndex) => {
42-
const iconUrl =
43-
option.icon && option.id ? `https://cdn.discordapp.com/icons/${option.id}/${option.icon}.png` : null;
54+
<DropdownMenu.Portal>
55+
<DropdownMenu.Content
56+
className="min-w-[220px] bg-card dark:bg-card-dark border border-on-secondary dark:border-on-secondary-dark rounded-lg p-1 shadow-lg z-50"
57+
sideOffset={5}
58+
>
59+
{options.map((option, optionIndex) => {
60+
const iconUrl =
61+
option.icon && option.id ? `https://cdn.discordapp.com/icons/${option.id}/${option.icon}.png` : null;
4462

45-
return (
46-
<DropdownMenu.Item asChild key={optionIndex}>
47-
<Link
48-
className="flex items-center gap-2 px-3 py-2 text-base text-secondary dark:text-secondary-dark hover:text-primary dark:hover:text-primary-dark hover:bg-on-tertiary dark:hover:bg-on-tertiary-dark rounded-md outline-none cursor-pointer"
49-
href={option.href}
50-
prefetch
51-
>
52-
{iconUrl ? (
53-
<Image
54-
alt={`${option.label} icon`}
55-
className="flex h-6 w-6 items-center justify-center rounded-full"
56-
height={24}
57-
src={iconUrl}
58-
width={24}
59-
/>
60-
) : (
61-
<span className="flex h-6 w-6 items-center justify-center overflow-hidden text-ellipsis whitespace-nowrap rounded-full bg-on-tertiary text-xs dark:bg-on-tertiary-dark">
62-
{getGuildAcronym(option.label)}
63-
</span>
64-
)}
65-
{option.label}
66-
</Link>
67-
</DropdownMenu.Item>
68-
);
69-
})}
70-
</DropdownMenu.Content>
71-
</DropdownMenu.Portal>
72-
</DropdownMenu.Root>
63+
return (
64+
<DropdownMenu.Item asChild key={optionIndex}>
65+
<Link
66+
className="flex items-center gap-2 px-3 py-2 text-base text-secondary dark:text-secondary-dark hover:text-primary dark:hover:text-primary-dark hover:bg-on-tertiary dark:hover:bg-on-tertiary-dark rounded-md outline-none cursor-pointer"
67+
href={option.href}
68+
prefetch
69+
>
70+
{iconUrl ? (
71+
<Image
72+
alt={`${option.label} icon`}
73+
className="flex h-6 w-6 items-center justify-center rounded-full"
74+
height={24}
75+
src={iconUrl}
76+
width={24}
77+
/>
78+
) : (
79+
<span className="flex h-6 w-6 items-center justify-center overflow-hidden text-ellipsis whitespace-nowrap rounded-full bg-on-tertiary text-xs dark:bg-on-tertiary-dark">
80+
{getGuildAcronym(option.label)}
81+
</span>
82+
)}
83+
{option.label}
84+
</Link>
85+
</DropdownMenu.Item>
86+
);
87+
})}
88+
</DropdownMenu.Content>
89+
</DropdownMenu.Portal>
90+
</DropdownMenu.Root>
91+
</div>
7392
);
7493
}

0 commit comments

Comments
 (0)