refactor: update styling and structure of team content, comment panel, and UI components

This commit is contained in:
Anish Sarkar 2026-05-19 17:34:59 +05:30
parent a5bd2bea9c
commit 8fd52d340f
6 changed files with 109 additions and 53 deletions

View file

@ -240,46 +240,77 @@ export function TeamContent({ searchSpaceId }: TeamContentProps) {
if (accessLoading || membersLoading) { if (accessLoading || membersLoading) {
return ( return (
<div className="space-y-6"> <div className="space-y-4 md:space-y-6">
<div className="flex items-center justify-between"> <div className="flex items-center gap-2 flex-wrap">
<Skeleton className="h-9 w-36 rounded-md" /> <Button
<Skeleton className="h-4 w-20" /> type="button"
variant="outline"
size="sm"
aria-disabled="true"
tabIndex={-1}
className="pointer-events-none gap-1.5 md:gap-2 text-xs md:text-sm bg-black text-white dark:bg-white dark:text-black"
>
<UserPlus className="h-3.5 w-3.5 md:h-4 md:w-4" />
Invite members
</Button>
<Button
type="button"
variant="secondary"
size="sm"
aria-disabled="true"
tabIndex={-1}
className="pointer-events-none gap-1.5 md:gap-2 text-xs md:text-sm"
>
<Link2 className="h-3.5 w-3.5 md:h-4 md:w-4 rotate-315" />
Active invites
<span className="inline-flex items-center justify-center h-4 md:h-5 min-w-4 md:min-w-5 px-1 rounded-full bg-neutral-700 text-neutral-200">
<Skeleton className="h-2.5 w-2.5 rounded-sm bg-neutral-500/60" />
</span>
</Button>
<div className="flex items-center gap-1 text-xs md:text-sm text-muted-foreground whitespace-nowrap">
<Skeleton className="h-3 w-2 rounded-sm" />
members
</div>
</div> </div>
<div className="rounded-lg border border-border/40 bg-background overflow-hidden"> <div className="rounded-lg border border-border/60 bg-accent overflow-hidden">
<Table className="table-fixed w-full"> <Table className="table-fixed w-full">
<TableHeader> <TableHeader>
<TableRow className="hover:bg-transparent border-b border-border/40"> <TableRow className="hover:bg-transparent border-b border-border/60">
<TableHead className="w-[45%] px-4 md:px-6 border-r border-border/40"> <TableHead className="w-[45%] px-4 md:px-6 border-r border-border/60">
<Skeleton className="h-3 w-16" /> <span className="flex items-center gap-1.5 text-sm font-medium text-muted-foreground/70">
<User size={14} className="opacity-60 text-muted-foreground" />
Name
</span>
</TableHead> </TableHead>
<TableHead className="hidden md:table-cell w-[25%] border-r border-border/40"> <TableHead className="hidden md:table-cell w-[25%] border-r border-border/60">
<Skeleton className="h-3 w-24" /> <span className="flex items-center gap-1.5 text-sm font-medium text-muted-foreground/70">
<Clock size={14} className="opacity-60 text-muted-foreground" />
Last logged in
</span>
</TableHead> </TableHead>
<TableHead className="w-[30%] px-4 md:px-6"> <TableHead className="w-[30%] px-4 md:px-6">
<div className="flex justify-end"> <span className="flex items-center gap-1.5 text-sm font-medium text-muted-foreground/70 justify-end">
<Skeleton className="h-3 w-12" /> <ShieldUser size={14} className="opacity-60 text-muted-foreground" />
</div> Role
</span>
</TableHead> </TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
{SKELETON_KEYS.map((id) => ( {SKELETON_KEYS.slice(0, 2).map((id) => (
<TableRow key={id} className="border-b border-border/40 hover:bg-transparent"> <TableRow key={id} className="border-b border-border/60 hover:bg-transparent">
<TableCell className="w-[45%] py-2.5 px-4 md:px-6 border-r border-border/40"> <TableCell className="w-[45%] py-2.5 px-4 md:px-6 border-r border-border/60">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Skeleton className="h-10 w-10 rounded-full shrink-0" /> <Skeleton className="h-10 w-10 rounded-full shrink-0" />
<div className="flex-1 min-w-0 space-y-1.5"> <Skeleton className="h-4 w-28 md:w-32" />
<Skeleton className="h-4 w-[60%]" />
<Skeleton className="h-3 w-[40%]" />
</div>
</div> </div>
</TableCell> </TableCell>
<TableCell className="hidden md:table-cell w-[25%] py-2.5 border-r border-border/40"> <TableCell className="hidden md:table-cell w-[25%] py-2.5 border-r border-border/60">
<Skeleton className="h-4 w-24" /> <Skeleton className="h-4 w-24" />
</TableCell> </TableCell>
<TableCell className="w-[30%] py-2.5 px-4 md:px-6"> <TableCell className="w-[30%] py-2.5 px-4 md:px-6">
<div className="flex justify-end"> <div className="flex justify-end">
<Skeleton className="h-4 w-16" /> <Skeleton className="h-4 w-12" />
</div> </div>
</TableCell> </TableCell>
</TableRow> </TableRow>
@ -294,41 +325,63 @@ export function TeamContent({ searchSpaceId }: TeamContentProps) {
return ( return (
<div className="space-y-4 md:space-y-6"> <div className="space-y-4 md:space-y-6">
<div className="flex items-center gap-2 flex-wrap"> <div className="flex items-center gap-2 flex-wrap">
{rolesLoading ? ( {canInvite &&
<Skeleton className="h-9 w-32 rounded-md" /> (rolesLoading ? (
) : ( <Button
canInvite && ( type="button"
variant="outline"
size="sm"
aria-disabled="true"
tabIndex={-1}
className="pointer-events-none gap-1.5 md:gap-2 text-xs md:text-sm bg-black text-white dark:bg-white dark:text-black"
>
<UserPlus className="h-3.5 w-3.5 md:h-4 md:w-4" />
Invite members
</Button>
) : (
<CreateInviteDialog <CreateInviteDialog
roles={roles} roles={roles}
onCreateInvite={handleCreateInvite} onCreateInvite={handleCreateInvite}
searchSpaceId={searchSpaceId} searchSpaceId={searchSpaceId}
/> />
) ))}
)} {canInvite &&
{invitesLoading ? ( (invitesLoading ? (
<Skeleton className="h-9 w-32 rounded-md" /> <Button
) : ( type="button"
canInvite && variant="secondary"
activeInvites.length > 0 && ( size="sm"
<AllInvitesDialog invites={activeInvites} onRevokeInvite={handleRevokeInvite} /> aria-disabled="true"
) tabIndex={-1}
)} className="pointer-events-none gap-1.5 md:gap-2 text-xs md:text-sm"
>
<Link2 className="h-3.5 w-3.5 md:h-4 md:w-4 rotate-315" />
Active invites
<span className="inline-flex items-center justify-center h-4 md:h-5 min-w-4 md:min-w-5 px-1 rounded-full bg-neutral-700 text-neutral-200">
<Skeleton className="h-2.5 w-2.5 rounded-sm bg-neutral-500/60" />
</span>
</Button>
) : (
activeInvites.length > 0 && (
<AllInvitesDialog invites={activeInvites} onRevokeInvite={handleRevokeInvite} />
)
))}
<p className="text-xs md:text-sm text-muted-foreground whitespace-nowrap"> <p className="text-xs md:text-sm text-muted-foreground whitespace-nowrap">
{members.length} {members.length === 1 ? "member" : "members"} {members.length} {members.length === 1 ? "member" : "members"}
</p> </p>
</div> </div>
<div className="rounded-lg border border-border/40 bg-background overflow-hidden"> <div className="rounded-lg border border-border/60 bg-accent overflow-hidden">
<Table className="table-fixed w-full"> <Table className="table-fixed w-full">
<TableHeader> <TableHeader>
<TableRow className="hover:bg-transparent border-b border-border/40"> <TableRow className="hover:bg-transparent border-b border-border/60">
<TableHead className="w-[45%] px-4 md:px-6 border-r border-border/40"> <TableHead className="w-[45%] px-4 md:px-6 border-r border-border/60">
<span className="flex items-center gap-1.5 text-sm font-medium text-muted-foreground/70"> <span className="flex items-center gap-1.5 text-sm font-medium text-muted-foreground/70">
<User size={14} className="opacity-60 text-muted-foreground" /> <User size={14} className="opacity-60 text-muted-foreground" />
Name Name
</span> </span>
</TableHead> </TableHead>
<TableHead className="hidden md:table-cell w-[25%] border-r border-border/40"> <TableHead className="hidden md:table-cell w-[25%] border-r border-border/60">
<span className="flex items-center gap-1.5 text-sm font-medium text-muted-foreground/70"> <span className="flex items-center gap-1.5 text-sm font-medium text-muted-foreground/70">
<Clock size={14} className="opacity-60 text-muted-foreground" /> <Clock size={14} className="opacity-60 text-muted-foreground" />
Last logged in Last logged in
@ -458,8 +511,8 @@ function MemberRow({
const showActions = !member.is_owner && (canManageRoles || canRemove); const showActions = !member.is_owner && (canManageRoles || canRemove);
return ( return (
<TableRow className="border-b border-border/40 transition-colors hover:bg-accent hover:text-accent-foreground"> <TableRow className="border-b border-border/60 transition-colors hover:bg-accent hover:text-accent-foreground">
<TableCell className="w-[45%] py-2.5 px-4 md:px-6 max-w-0 border-r border-border/40"> <TableCell className="w-[45%] py-2.5 px-4 md:px-6 max-w-0 border-r border-border/60">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Avatar className="size-10 shrink-0"> <Avatar className="size-10 shrink-0">
{member.user_avatar_url && ( {member.user_avatar_url && (
@ -478,7 +531,7 @@ function MemberRow({
</div> </div>
</TableCell> </TableCell>
<TableCell className="hidden md:table-cell w-[25%] py-2.5 text-sm text-foreground border-r border-border/40"> <TableCell className="hidden md:table-cell w-[25%] py-2.5 text-sm text-foreground border-r border-border/60">
{member.user_last_login ? formatRelativeDate(member.user_last_login) : "Never"} {member.user_last_login ? formatRelativeDate(member.user_last_login) : "Never"}
</TableCell> </TableCell>
@ -542,7 +595,7 @@ function MemberRow({
</AlertDialogContent> </AlertDialogContent>
</AlertDialog> </AlertDialog>
)} )}
<DropdownMenuSeparator className="dark:bg-white/5" /> <DropdownMenuSeparator className="bg-popover-border" />
<DropdownMenuItem <DropdownMenuItem
onClick={() => onClick={() =>
router.push(`/dashboard/${searchSpaceId}/search-space-settings/team-roles`) router.push(`/dashboard/${searchSpaceId}/search-space-settings/team-roles`)
@ -710,7 +763,7 @@ function CreateInviteDialog({
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="invite-role">Role</Label> <Label htmlFor="invite-role">Role</Label>
<Select value={roleId} onValueChange={setRoleId}> <Select value={roleId} onValueChange={setRoleId}>
<SelectTrigger> <SelectTrigger className="border-popover-border">
<SelectValue placeholder="Assign a role" /> <SelectValue placeholder="Assign a role" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@ -746,7 +799,7 @@ function CreateInviteDialog({
<Button <Button
variant="outline" variant="outline"
className={cn( className={cn(
"w-full justify-start text-left font-normal bg-transparent", "w-full justify-start text-left font-normal bg-transparent border-popover-border",
!expiresAt && "text-muted-foreground" !expiresAt && "text-muted-foreground"
)} )}
> >

View file

@ -1,6 +1,7 @@
"use client"; "use client";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { Separator } from "@/components/ui/separator";
import { CommentComposer } from "../comment-composer/comment-composer"; import { CommentComposer } from "../comment-composer/comment-composer";
import { CommentThread } from "../comment-thread/comment-thread"; import { CommentThread } from "../comment-thread/comment-thread";
import type { CommentPanelProps } from "./types"; import type { CommentPanelProps } from "./types";
@ -85,7 +86,9 @@ export function CommentPanel({
</div> </div>
)} )}
<div className={cn("p-3", isMobile && "fixed bottom-0 left-0 right-0 z-50 bg-card border-t")}> {hasThreads && !isMobile ? <Separator className="bg-sidebar-border" /> : null}
<div className={cn("p-3", isMobile && "fixed bottom-0 left-0 right-0 z-50 bg-sidebar")}>
<CommentComposer <CommentComposer
members={members} members={members}
membersLoading={membersLoading} membersLoading={membersLoading}

View file

@ -24,7 +24,7 @@ function Calendar({
<DayPicker <DayPicker
showOutsideDays={showOutsideDays} showOutsideDays={showOutsideDays}
className={cn( className={cn(
"bg-background group/calendar p-3 [--cell-size:--spacing(8)] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent", "bg-background group/calendar rounded-md border border-popover-border p-3 [--cell-size:--spacing(8)] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent",
String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`, String.raw`rtl:**:[.rdp-button\_next>svg]:rotate-180`,
String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`, String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`,
className className
@ -61,7 +61,7 @@ function Calendar({
defaultClassNames.dropdowns defaultClassNames.dropdowns
), ),
dropdown_root: cn( dropdown_root: cn(
"relative has-focus:border-ring border border-input shadow-xs has-focus:ring-ring/50 has-focus:ring-[3px] rounded-md", "relative has-focus:border-ring border border-popover-border shadow-xs has-focus:ring-ring/50 has-focus:ring-[3px] rounded-md",
defaultClassNames.dropdown_root defaultClassNames.dropdown_root
), ),
dropdown: cn("absolute inset-0 opacity-0", defaultClassNames.dropdown), dropdown: cn("absolute inset-0 opacity-0", defaultClassNames.dropdown),

View file

@ -8,7 +8,7 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
type={type} type={type}
data-slot="input" data-slot="input"
className={cn( className={cn(
"border-input file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", "border-popover-border file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
"focus-visible:border-ring", "focus-visible:border-ring",
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
className className

View file

@ -27,7 +27,7 @@ function SelectTrigger({
<SelectPrimitive.Trigger <SelectPrimitive.Trigger
data-slot="select-trigger" data-slot="select-trigger"
className={cn( className={cn(
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive flex h-9 w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-0 focus-visible:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "border-popover-border data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive flex h-9 w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-0 focus-visible:ring-offset-0 disabled:cursor-not-allowed disabled:opacity-50 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className className
)} )}
{...props} {...props}
@ -51,7 +51,7 @@ function SelectContent({
<SelectPrimitive.Content <SelectPrimitive.Content
data-slot="select-content" data-slot="select-content"
className={cn( className={cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md shadow-md", "bg-popover text-popover-foreground border border-popover-border data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md shadow-md",
position === "popper" && position === "popper" &&
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
className className

View file

@ -7,7 +7,7 @@ function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
<textarea <textarea
data-slot="textarea" data-slot="textarea"
className={cn( className={cn(
"border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm", "border-popover-border placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
className className
)} )}
{...props} {...props}