style: Adjust UI element and text sizes for improved responsiveness and visual consistency across various components.

This commit is contained in:
Anish Sarkar 2025-12-28 23:59:01 +05:30
parent 3bea989868
commit 1d5ee45bd9
9 changed files with 145 additions and 149 deletions

View file

@ -51,11 +51,11 @@ export default function AddSourcesPage() {
> >
{/* Header */} {/* Header */}
<div className="text-center space-y-2"> <div className="text-center space-y-2">
<h1 className="text-4xl font-bold tracking-tight flex items-center justify-center gap-3"> <h1 className="text-2xl sm:text-4xl font-bold tracking-tight flex items-center justify-center gap-3">
<Database className="h-8 w-8" /> <Database className="h-6 w-6 sm:h-8 sm:w-8" />
Add Sources Add Sources
</h1> </h1>
<p className="text-muted-foreground text-lg">Add your sources to your search space</p> <p className="text-muted-foreground text-sm sm:text-lg">Add your sources to your search space</p>
</div> </div>
{/* Tabs */} {/* Tabs */}

View file

@ -1,29 +1,15 @@
"use client"; "use client";
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import {
type ColumnDef,
type ColumnFiltersState,
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
type SortingState,
useReactTable,
} from "@tanstack/react-table";
import { useAtomValue } from "jotai"; import { useAtomValue } from "jotai";
import { import {
ArrowLeft, ArrowLeft,
Calendar, Calendar,
Check, Check,
ChevronDown,
ChevronUp,
Clock, Clock,
Copy, Copy,
Crown, Crown,
Edit2, Edit2,
ExternalLink,
Hash, Hash,
Link2, Link2,
LinkIcon, LinkIcon,
@ -32,7 +18,6 @@ import {
Plus, Plus,
RefreshCw, RefreshCw,
Search, Search,
Settings,
Shield, Shield,
ShieldCheck, ShieldCheck,
Trash2, Trash2,
@ -40,7 +25,6 @@ import {
UserMinus, UserMinus,
UserPlus, UserPlus,
Users, Users,
X,
} from "lucide-react"; } from "lucide-react";
import { motion } from "motion/react"; import { motion } from "motion/react";
import { useParams, useRouter } from "next/navigation"; import { useParams, useRouter } from "next/navigation";
@ -90,7 +74,6 @@ import {
DropdownMenu, DropdownMenu,
DropdownMenuContent, DropdownMenuContent,
DropdownMenuItem, DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator, DropdownMenuSeparator,
DropdownMenuTrigger, DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"; } from "@/components/ui/dropdown-menu";
@ -105,7 +88,6 @@ import {
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from "@/components/ui/select"; } from "@/components/ui/select";
import { Separator } from "@/components/ui/separator";
import { import {
Table, Table,
TableBody, TableBody,
@ -295,7 +277,7 @@ export default function TeamManagementPage() {
staleTime: 5 * 60 * 1000, staleTime: 5 * 60 * 1000,
}); });
const { data: permissionsData, isLoading: permissionsLoading } = useAtomValue(permissionsAtom); const { data: permissionsData } = useAtomValue(permissionsAtom);
const permissions = permissionsData?.permissions || []; const permissions = permissionsData?.permissions || [];
const groupedPermissions = useMemo(() => { const groupedPermissions = useMemo(() => {
const groups: Record<string, typeof permissions> = {}; const groups: Record<string, typeof permissions> = {};
@ -308,8 +290,6 @@ export default function TeamManagementPage() {
return groups; return groups;
}, [permissions]); }, [permissions]);
const canManageMembers = hasPermission("members:view");
const canManageRoles = hasPermission("roles:read");
const canInvite = hasPermission("members:invite"); const canInvite = hasPermission("members:invite");
const handleRefresh = useCallback(async () => { const handleRefresh = useCallback(async () => {
@ -339,40 +319,40 @@ export default function TeamManagementPage() {
variants={staggerContainer} variants={staggerContainer}
className="min-h-screen bg-background" className="min-h-screen bg-background"
> >
<div className="container max-w-7xl mx-auto p-6 lg:p-8"> <div className="container max-w-7xl mx-auto p-4 md:p-6 lg:p-8">
<motion.div variants={fadeInUp} className="space-y-8"> <motion.div variants={fadeInUp} className="space-y-8">
{/* Header */} {/* Header */}
<div className="space-y-4"> <div className="space-y-4">
<div className="flex items-center justify-between"> <div className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
<div className="flex items-center space-x-4"> <div className="flex items-start space-x-3 md:items-center md:space-x-4">
<button <button
onClick={() => router.push(`/dashboard/${searchSpaceId}`)} onClick={() => router.push(`/dashboard/${searchSpaceId}`)}
className="flex items-center justify-center h-10 w-10 rounded-lg bg-primary/10 hover:bg-primary/20 transition-colors" className="flex items-center justify-center h-9 w-9 md:h-10 md:w-10 rounded-lg bg-primary/10 hover:bg-primary/20 transition-colors shrink-0"
aria-label="Back to Dashboard" aria-label="Back to Dashboard"
type="button" type="button"
> >
<ArrowLeft className="h-5 w-5 text-primary" /> <ArrowLeft className="h-4 w-4 md:h-5 md:w-5 text-primary" />
</button> </button>
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-gradient-to-br from-primary/20 to-primary/5 ring-1 ring-primary/10"> <div className="flex h-10 w-10 md:h-12 md:w-12 items-center justify-center rounded-xl bg-gradient-to-br from-primary/20 to-primary/5 ring-1 ring-primary/10 shrink-0">
<Users className="h-6 w-6 text-primary" /> <Users className="h-5 w-5 md:h-6 md:w-6 text-primary" />
</div> </div>
<div className="space-y-1"> <div className="space-y-1 min-w-0">
<h1 className="text-3xl font-bold tracking-tight bg-gradient-to-r from-foreground to-foreground/70 bg-clip-text"> <h1 className="text-2xl md:text-3xl font-bold tracking-tight bg-gradient-to-r from-foreground to-foreground/70 bg-clip-text">
Team Management Team Management
</h1> </h1>
<p className="text-muted-foreground"> <p className="text-xs md:text-sm text-muted-foreground">
Manage members, roles, and invite links for your search space Manage members, roles, and invite links for your search space
</p> </p>
</div> </div>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Button onClick={handleRefresh} variant="outline" size="sm" className="gap-2"> <Button onClick={handleRefresh} variant="outline" size="sm" className="gap-2 w-full md:w-auto">
<RefreshCw className="h-4 w-4" /> <RefreshCw className="h-4 w-4" />
Refresh Refresh
</Button> </Button>
</div> </div>
</div> </div>
<Separator className="bg-gradient-to-r from-border via-border/50 to-transparent" />
</div> </div>
{/* Summary Cards */} {/* Summary Cards */}
@ -435,42 +415,46 @@ export default function TeamManagementPage() {
{/* Tabs Content */} {/* Tabs Content */}
<Tabs value={activeTab} onValueChange={setActiveTab} className="space-y-6"> <Tabs value={activeTab} onValueChange={setActiveTab} className="space-y-6">
<div className="flex items-center justify-between"> <div className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
<TabsList className="bg-muted/50 p-1"> <div className="overflow-x-auto pb-1 md:pb-0">
<TabsTrigger value="members" className="gap-2 data-[state=active]:bg-background"> <TabsList className="bg-muted/50 p-1 w-full md:w-fit grid grid-cols-3 md:flex">
<Users className="h-4 w-4" /> <TabsTrigger value="members" className="gap-1.5 md:gap-2 data-[state=active]:bg-background whitespace-nowrap w-full text-xs md:text-sm flex-1">
<span>Members</span> <Users className="h-4 w-4 hidden md:block" />
<Badge variant="secondary" className="ml-1 text-xs"> <span>Members</span>
{members.length} <Badge variant="secondary" className="ml-1 text-xs">
</Badge> {members.length}
</TabsTrigger> </Badge>
<TabsTrigger value="roles" className="gap-2 data-[state=active]:bg-background"> </TabsTrigger>
<Shield className="h-4 w-4" /> <TabsTrigger value="roles" className="gap-1.5 md:gap-2 data-[state=active]:bg-background whitespace-nowrap w-full text-xs md:text-sm flex-1">
<span>Roles</span> <Shield className="h-4 w-4 hidden md:block" />
<Badge variant="secondary" className="ml-1 text-xs"> <span>Roles</span>
{roles.length} <Badge variant="secondary" className="ml-1 text-xs">
</Badge> {roles.length}
</TabsTrigger> </Badge>
<TabsTrigger value="invites" className="gap-2 data-[state=active]:bg-background"> </TabsTrigger>
<LinkIcon className="h-4 w-4" /> <TabsTrigger value="invites" className="gap-1.5 md:gap-2 data-[state=active]:bg-background whitespace-nowrap w-full text-xs md:text-sm flex-1">
<span>Invites</span> <LinkIcon className="h-4 w-4 hidden md:block" />
<Badge variant="secondary" className="ml-1 text-xs"> <span>Invites</span>
{invites.filter((i) => i.is_active).length} <Badge variant="secondary" className="ml-1 text-xs">
</Badge> {invites.filter((i) => i.is_active).length}
</TabsTrigger> </Badge>
</TabsList> </TabsTrigger>
</TabsList>
</div>
{activeTab === "invites" && canInvite && ( {activeTab === "invites" && canInvite && (
<CreateInviteDialog <CreateInviteDialog
roles={roles} roles={roles}
onCreateInvite={handleCreateInvite} onCreateInvite={handleCreateInvite}
searchSpaceId={searchSpaceId} searchSpaceId={searchSpaceId}
className="w-full md:w-auto"
/> />
)} )}
{activeTab === "roles" && hasPermission("roles:create") && ( {activeTab === "roles" && hasPermission("roles:create") && (
<CreateRoleDialog <CreateRoleDialog
groupedPermissions={groupedPermissions} groupedPermissions={groupedPermissions}
onCreateRole={handleCreateRole} onCreateRole={handleCreateRole}
className="w-full md:w-auto"
/> />
)} )}
</div> </div>
@ -533,8 +517,6 @@ function MembersTab({
canManageRoles: boolean; canManageRoles: boolean;
canRemove: boolean; canRemove: boolean;
}) { }) {
const [sorting, setSorting] = useState<SortingState>([]);
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
const [searchQuery, setSearchQuery] = useState(""); const [searchQuery, setSearchQuery] = useState("");
const filteredMembers = useMemo(() => { const filteredMembers = useMemo(() => {
@ -575,13 +557,13 @@ function MembersTab({
</div> </div>
{/* Members List */} {/* Members List */}
<div className="rounded-lg border bg-card overflow-hidden"> <div className="rounded-lg border bg-card overflow-x-auto">
<Table> <Table>
<TableHeader> <TableHeader>
<TableRow className="bg-muted/50"> <TableRow className="bg-muted/50">
<TableHead className="w-[300px]">Member</TableHead> <TableHead className="w-auto md:w-[300px] px-2 md:px-4">Member</TableHead>
<TableHead>Role</TableHead> <TableHead className="px-2 md:px-4">Role</TableHead>
<TableHead>Joined</TableHead> <TableHead className="hidden md:table-cell">Joined</TableHead>
<TableHead className="text-right">Actions</TableHead> <TableHead className="text-right">Actions</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
@ -604,11 +586,11 @@ function MembersTab({
transition={{ delay: index * 0.05 }} transition={{ delay: index * 0.05 }}
className="group border-b transition-colors hover:bg-muted/50" className="group border-b transition-colors hover:bg-muted/50"
> >
<TableCell> <TableCell className="py-2 px-2 md:py-4 md:px-4 align-middle">
<div className="flex items-center gap-3"> <div className="flex items-center gap-1.5 md:gap-3">
<div className="relative"> <div className="relative">
<div className="h-10 w-10 rounded-full bg-gradient-to-br from-primary/20 to-primary/5 flex items-center justify-center ring-2 ring-background"> <div className="h-8 w-8 md:h-10 md:w-10 rounded-full bg-gradient-to-br from-primary/20 to-primary/5 flex items-center justify-center ring-2 ring-background">
<User className="h-5 w-5 text-primary" /> <User className="h-4 w-4 md:h-5 md:w-5 text-primary" />
</div> </div>
{member.is_owner && ( {member.is_owner && (
<div className="absolute -top-1 -right-1 h-5 w-5 rounded-full bg-amber-500 flex items-center justify-center ring-2 ring-background"> <div className="absolute -top-1 -right-1 h-5 w-5 rounded-full bg-amber-500 flex items-center justify-center ring-2 ring-background">
@ -616,12 +598,14 @@ function MembersTab({
</div> </div>
)} )}
</div> </div>
<div> <div className="min-w-0">
<p className="font-medium">{member.user_email || "Unknown"}</p> <p className="font-medium text-xs md:text-sm truncate">
{member.user_email || "Unknown"}
</p>
{member.is_owner && ( {member.is_owner && (
<Badge <Badge
variant="outline" variant="outline"
className="text-xs mt-1 bg-amber-500/10 text-amber-600 border-amber-500/20" className="text-[10px] md:text-xs mt-0.5 md:mt-1 bg-amber-500/10 text-amber-600 border-amber-500/20 hidden md:inline-flex"
> >
Owner Owner
</Badge> </Badge>
@ -629,7 +613,7 @@ function MembersTab({
</div> </div>
</div> </div>
</TableCell> </TableCell>
<TableCell> <TableCell className="py-2 px-2 md:py-4 md:px-4 align-middle">
{canManageRoles && !member.is_owner ? ( {canManageRoles && !member.is_owner ? (
<Select <Select
value={member.role_id?.toString() || "none"} value={member.role_id?.toString() || "none"}
@ -637,7 +621,7 @@ function MembersTab({
onUpdateRole(member.id, value === "none" ? null : Number(value)) onUpdateRole(member.id, value === "none" ? null : Number(value))
} }
> >
<SelectTrigger className="w-[180px]"> <SelectTrigger className="w-full md:w-[180px] h-8 md:h-10 text-xs md:text-sm">
<SelectValue placeholder="Select role" /> <SelectValue placeholder="Select role" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@ -653,19 +637,19 @@ function MembersTab({
</SelectContent> </SelectContent>
</Select> </Select>
) : ( ) : (
<Badge variant="secondary" className="gap-1"> <Badge variant="secondary" className="gap-1 text-[10px] md:text-xs py-0 md:py-0.5">
<Shield className="h-3 w-3" /> <Shield className="h-2.5 w-2.5 md:h-3 md:w-3" />
{member.role?.name || "No role"} {member.role?.name || "No role"}
</Badge> </Badge>
)} )}
</TableCell> </TableCell>
<TableCell> <TableCell className="hidden md:table-cell">
<div className="flex items-center gap-2 text-sm text-muted-foreground"> <div className="flex items-center gap-2 text-sm text-muted-foreground">
<Calendar className="h-4 w-4" /> <Calendar className="h-4 w-4" />
{new Date(member.joined_at).toLocaleDateString()} {new Date(member.joined_at).toLocaleDateString()}
</div> </div>
</TableCell> </TableCell>
<TableCell className="text-right"> <TableCell className="text-right py-2 px-2 md:py-4 md:px-4 align-middle">
{canRemove && !member.is_owner && ( {canRemove && !member.is_owner && (
<AlertDialog> <AlertDialog>
<AlertDialogTrigger asChild> <AlertDialogTrigger asChild>
@ -962,11 +946,11 @@ function InvitesTab({
className={cn("relative overflow-hidden transition-all", isInactive && "opacity-60")} className={cn("relative overflow-hidden transition-all", isInactive && "opacity-60")}
> >
<CardContent className="p-4"> <CardContent className="p-4">
<div className="flex items-center justify-between gap-4"> <div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div className="flex items-center gap-4 flex-1 min-w-0"> <div className="flex items-start md:items-center gap-4 flex-1 min-w-0">
<div <div
className={cn( className={cn(
"h-12 w-12 rounded-xl flex items-center justify-center shrink-0", "h-10 w-10 md:h-12 md:w-12 rounded-xl flex items-center justify-center shrink-0",
invite.is_active && !isExpired && !isMaxedOut invite.is_active && !isExpired && !isMaxedOut
? "bg-emerald-500/20" ? "bg-emerald-500/20"
: "bg-muted" : "bg-muted"
@ -974,7 +958,7 @@ function InvitesTab({
> >
<Link2 <Link2
className={cn( className={cn(
"h-6 w-6", "h-5 w-5 md:h-6 md:w-6",
invite.is_active && !isExpired && !isMaxedOut invite.is_active && !isExpired && !isMaxedOut
? "text-emerald-600" ? "text-emerald-600"
: "text-muted-foreground" : "text-muted-foreground"
@ -991,7 +975,7 @@ function InvitesTab({
)} )}
{isMaxedOut && ( {isMaxedOut && (
<Badge variant="secondary" className="text-xs"> <Badge variant="secondary" className="text-xs">
Max uses reached Maxed
</Badge> </Badge>
)} )}
{!invite.is_active && !isExpired && !isMaxedOut && ( {!invite.is_active && !isExpired && !isMaxedOut && (
@ -1000,44 +984,44 @@ function InvitesTab({
</Badge> </Badge>
)} )}
</div> </div>
<div className="flex items-center gap-4 mt-1 text-sm text-muted-foreground flex-wrap"> <div className="flex flex-col md:flex-row md:items-center gap-2 md:gap-4 mt-1 text-sm text-muted-foreground">
<span className="flex items-center gap-1"> <span className="flex items-center gap-1">
<Shield className="h-3 w-3" /> <Shield className="h-3 w-3" />
{invite.role?.name || "Default role"} {invite.role?.name || "Default role"}
</span> </span>
<span className="flex items-center gap-1"> <span className="flex items-center gap-1">
<Hash className="h-3 w-3" /> <Hash className="h-3 w-3" />
{invite.uses_count} uses {invite.uses_count}
{invite.max_uses && ` / ${invite.max_uses}`} {invite.max_uses ? ` / ${invite.max_uses} uses` : " uses"}
</span> </span>
{invite.expires_at && ( {invite.expires_at && (
<span className="flex items-center gap-1"> <span className="flex items-center gap-1">
<Clock className="h-3 w-3" /> <Clock className="h-3 w-3" />
{isExpired {isExpired
? "Expired" ? "Expired"
: `Expires ${new Date(invite.expires_at).toLocaleDateString()}`} : `Exp: ${new Date(invite.expires_at).toLocaleDateString()}`}
</span> </span>
)} )}
</div> </div>
</div> </div>
</div> </div>
<div className="flex items-center gap-2 shrink-0"> <div className="flex items-center gap-2 shrink-0 self-end md:self-auto">
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
className="gap-2" className="gap-2 flex-1 md:flex-none"
onClick={() => copyInviteLink(invite)} onClick={() => copyInviteLink(invite)}
disabled={Boolean(isInactive)} disabled={Boolean(isInactive)}
> >
{copiedId === invite.id ? ( {copiedId === invite.id ? (
<> <>
<Check className="h-4 w-4 text-emerald-500" /> <Check className="h-4 w-4 text-emerald-500" />
Copied! <span className="md:inline">Copied!</span>
</> </>
) : ( ) : (
<> <>
<Copy className="h-4 w-4" /> <Copy className="h-4 w-4" />
Copy Link <span className="md:inline">Copy</span>
</> </>
)} )}
</Button> </Button>
@ -1088,11 +1072,11 @@ function InvitesTab({
function CreateInviteDialog({ function CreateInviteDialog({
roles, roles,
onCreateInvite, onCreateInvite,
searchSpaceId, className,
}: { }: {
roles: Role[]; roles: Role[];
onCreateInvite: (data: CreateInviteRequest["data"]) => Promise<Invite>; onCreateInvite: (data: CreateInviteRequest["data"]) => Promise<Invite>;
searchSpaceId: number; className?: string;
}) { }) {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [creating, setCreating] = useState(false); const [creating, setCreating] = useState(false);
@ -1142,12 +1126,12 @@ function CreateInviteDialog({
return ( return (
<Dialog open={open} onOpenChange={(v) => (v ? setOpen(true) : handleClose())}> <Dialog open={open} onOpenChange={(v) => (v ? setOpen(true) : handleClose())}>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button className="gap-2"> <Button className={cn("gap-2", className)}>
<UserPlus className="h-4 w-4" /> <UserPlus className="h-4 w-4" />
Create Invite Create Invite
</Button> </Button>
</DialogTrigger> </DialogTrigger>
<DialogContent className="sm:max-w-md"> <DialogContent className="w-[92vw] max-w-[92vw] sm:max-w-md p-4 md:p-6">
{createdInvite ? ( {createdInvite ? (
<> <>
<DialogHeader> <DialogHeader>
@ -1159,7 +1143,7 @@ function CreateInviteDialog({
Share this link to invite people to your search space. Share this link to invite people to your search space.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="space-y-4 py-4"> <div className="space-y-3 py-2 md:py-4">
<div className="flex items-center gap-2 p-3 bg-muted rounded-lg"> <div className="flex items-center gap-2 p-3 bg-muted rounded-lg">
<code className="flex-1 min-w-0 text-sm break-all"> <code className="flex-1 min-w-0 text-sm break-all">
{window.location.origin}/invite/{createdInvite.invite_code} {window.location.origin}/invite/{createdInvite.invite_code}
@ -1203,7 +1187,7 @@ function CreateInviteDialog({
Create a link to invite people to this search space. Create a link to invite people to this search space.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="space-y-4 py-4"> <div className="space-y-3 py-2 md:py-4">
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="invite-name">Name (optional)</Label> <Label htmlFor="invite-name">Name (optional)</Label>
<Input <Input
@ -1234,7 +1218,7 @@ function CreateInviteDialog({
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
<div className="grid grid-cols-2 gap-4"> <div className="flex flex-col md:grid md:grid-cols-2 gap-3 md:gap-4">
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="max-uses">Max uses (optional)</Label> <Label htmlFor="max-uses">Max uses (optional)</Label>
<Input <Input
@ -1301,9 +1285,11 @@ function CreateInviteDialog({
function CreateRoleDialog({ function CreateRoleDialog({
groupedPermissions, groupedPermissions,
onCreateRole, onCreateRole,
className,
}: { }: {
groupedPermissions: Record<string, { value: string; name: string; category: string }[]>; groupedPermissions: Record<string, { value: string; name: string; category: string }[]>;
onCreateRole: (data: CreateRoleRequest["data"]) => Promise<Role>; onCreateRole: (data: CreateRoleRequest["data"]) => Promise<Role>;
className?: string;
}) { }) {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [creating, setCreating] = useState(false); const [creating, setCreating] = useState(false);
@ -1358,20 +1344,20 @@ function CreateRoleDialog({
return ( return (
<Dialog open={open} onOpenChange={setOpen}> <Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button className="gap-2"> <Button className={cn("gap-2", className)}>
<Plus className="h-4 w-4" /> <Plus className="h-4 w-4" />
Create Role Create Role
</Button> </Button>
</DialogTrigger> </DialogTrigger>
<DialogContent className="sm:max-w-xl"> <DialogContent className="w-[92vw] max-w-[92vw] sm:max-w-xl p-4 md:p-6">
<DialogHeader> <DialogHeader>
<DialogTitle>Create Custom Role</DialogTitle> <DialogTitle>Create Custom Role</DialogTitle>
<DialogDescription> <DialogDescription className="text-xs md:text-sm">
Define a new role with specific permissions for this search space. Define a new role with specific permissions for this search space.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="space-y-4 py-4"> <div className="space-y-3 py-2 md:py-4">
<div className="grid grid-cols-2 gap-4"> <div className="flex flex-col md:grid md:grid-cols-2 gap-3 md:gap-4">
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="role-name">Role Name *</Label> <Label htmlFor="role-name">Role Name *</Label>
<Input <Input

View file

@ -69,7 +69,7 @@ export default function InferenceParamsEditor({ params, setParams }: InferencePa
<div className="space-y-6 p-2 sm:p-0"> <div className="space-y-6 p-2 sm:p-0">
<div className="grid grid-cols-1 gap-4 md:grid-cols-[1fr_1fr_auto] md:gap-3 items-end"> <div className="grid grid-cols-1 gap-4 md:grid-cols-[1fr_1fr_auto] md:gap-3 items-end">
<div className="flex flex-col space-y-1"> <div className="flex flex-col space-y-1">
<Label htmlFor="param-key" className="text-sm font-medium"> <Label htmlFor="param-key" className="text-xs sm:text-sm font-medium">
Parameter Key Parameter Key
</Label> </Label>
<Select value={selectedKey} onValueChange={setSelectedKey}> <Select value={selectedKey} onValueChange={setSelectedKey}>
@ -87,7 +87,7 @@ export default function InferenceParamsEditor({ params, setParams }: InferencePa
</div> </div>
<div className="flex flex-col space-y-1"> <div className="flex flex-col space-y-1">
<Label htmlFor="param-value" className="text-sm font-medium"> <Label htmlFor="param-value" className="text-xs sm:text-sm font-medium">
Value Value
</Label> </Label>
<Input <Input
@ -100,11 +100,11 @@ export default function InferenceParamsEditor({ params, setParams }: InferencePa
</div> </div>
<Button <Button
className="w-full md:w-auto h-10 mt-0" className="w-full md:w-auto h-9 sm:h-10 mt-0 text-xs sm:text-sm"
onClick={handleAdd} onClick={handleAdd}
disabled={!selectedKey || value === ""} disabled={!selectedKey || value === ""}
> >
<Plus className="w-4 h-4 mr-2" /> Add Parameter <Plus className="w-3.5 h-3.5 sm:w-4 sm:h-4 mr-2" /> Add Parameter
</Button> </Button>
</div> </div>

View file

@ -184,7 +184,7 @@ export function ModelConfigSidebar({
<Bot className="size-5 text-primary" /> <Bot className="size-5 text-primary" />
</div> </div>
<div> <div>
<h2 className="text-lg font-semibold">{getTitle()}</h2> <h2 className="text-base sm:text-lg font-semibold">{getTitle()}</h2>
<div className="flex items-center gap-2 mt-0.5"> <div className="flex items-center gap-2 mt-0.5">
{isGlobal ? ( {isGlobal ? (
<Badge variant="secondary" className="gap-1 text-xs"> <Badge variant="secondary" className="gap-1 text-xs">
@ -207,9 +207,10 @@ export function ModelConfigSidebar({
variant="ghost" variant="ghost"
size="icon" size="icon"
onClick={() => onOpenChange(false)} onClick={() => onOpenChange(false)}
className="rounded-xl hover:bg-destructive/10 hover:text-destructive" className="h-8 w-8 rounded-full"
> >
<X className="size-5" /> <X className="h-4 w-4" />
<span className="sr-only">Close</span>
</Button> </Button>
</div> </div>

View file

@ -352,9 +352,9 @@ export function SourceDetailPanel({
size="icon" size="icon"
variant="ghost" variant="ghost"
onClick={() => onOpenChange(false)} onClick={() => onOpenChange(false)}
className="rounded-xl h-10 w-10 hover:bg-destructive/10 hover:text-destructive transition-colors" className="h-8 w-8 rounded-full"
> >
<X className="h-5 w-5" /> <X className="h-4 w-4" />
<span className="sr-only">Close</span> <span className="sr-only">Close</span>
</Button> </Button>
</div> </div>

View file

@ -156,8 +156,8 @@ export function LLMConfigForm({
<form onSubmit={form.handleSubmit(handleFormSubmit)} className="space-y-6"> <form onSubmit={form.handleSubmit(handleFormSubmit)} className="space-y-6">
{/* System Instructions & Citations Section */} {/* System Instructions & Citations Section */}
<div className="space-y-4"> <div className="space-y-4">
<div className="flex items-center gap-2 text-sm font-medium text-muted-foreground"> <div className="flex items-center gap-2 text-xs sm:text-sm font-medium text-muted-foreground">
<MessageSquareQuote className="h-4 w-4" /> <MessageSquareQuote className="h-3.5 w-3.5 sm:h-4 sm:w-4" />
System Instructions System Instructions
</div> </div>
@ -168,7 +168,7 @@ export function LLMConfigForm({
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<FormLabel>Instructions for the AI</FormLabel> <FormLabel className="text-xs sm:text-sm">Instructions for the AI</FormLabel>
{defaultInstructions && ( {defaultInstructions && (
<Button <Button
type="button" type="button"
@ -177,7 +177,7 @@ export function LLMConfigForm({
onClick={() => onClick={() =>
field.onChange(defaultInstructions.default_system_instructions) field.onChange(defaultInstructions.default_system_instructions)
} }
className="h-7 text-xs text-muted-foreground hover:text-foreground" className="h-7 text-[10px] sm:text-xs text-muted-foreground hover:text-foreground"
> >
Reset to Default Reset to Default
</Button> </Button>
@ -187,11 +187,11 @@ export function LLMConfigForm({
<Textarea <Textarea
placeholder="Enter system instructions for the AI..." placeholder="Enter system instructions for the AI..."
rows={6} rows={6}
className="font-mono text-xs resize-none" className="font-mono text-[11px] sm:text-xs resize-none"
{...field} {...field}
/> />
</FormControl> </FormControl>
<FormDescription className="text-xs"> <FormDescription className="text-[10px] sm:text-xs">
Use {"{resolved_today}"} to include today's date dynamically Use {"{resolved_today}"} to include today's date dynamically
</FormDescription> </FormDescription>
<FormMessage /> <FormMessage />
@ -206,8 +206,8 @@ export function LLMConfigForm({
render={({ field }) => ( render={({ field }) => (
<FormItem className="flex items-center justify-between rounded-lg border p-3 bg-muted/30"> <FormItem className="flex items-center justify-between rounded-lg border p-3 bg-muted/30">
<div className="space-y-0.5"> <div className="space-y-0.5">
<FormLabel className="text-sm font-medium">Enable Citations</FormLabel> <FormLabel className="text-xs sm:text-sm font-medium">Enable Citations</FormLabel>
<FormDescription className="text-xs"> <FormDescription className="text-[10px] sm:text-xs">
Include [citation:id] references to source documents Include [citation:id] references to source documents
</FormDescription> </FormDescription>
</div> </div>
@ -223,8 +223,8 @@ export function LLMConfigForm({
{/* Model Configuration Section */} {/* Model Configuration Section */}
<div className="space-y-4"> <div className="space-y-4">
<div className="flex items-center gap-2 text-sm font-medium text-muted-foreground"> <div className="flex items-center gap-2 text-xs sm:text-sm font-medium text-muted-foreground">
<Bot className="h-4 w-4" /> <Bot className="h-3.5 w-3.5 sm:h-4 sm:w-4" />
Model Configuration Model Configuration
</div> </div>
@ -235,7 +235,7 @@ export function LLMConfigForm({
name="name" name="name"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="flex items-center gap-2"> <FormLabel className="flex items-center gap-2 text-xs sm:text-sm">
<Sparkles className="h-3.5 w-3.5 text-violet-500" /> <Sparkles className="h-3.5 w-3.5 text-violet-500" />
Configuration Name Configuration Name
</FormLabel> </FormLabel>
@ -256,7 +256,7 @@ export function LLMConfigForm({
name="description" name="description"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="text-muted-foreground"> <FormLabel className="text-muted-foreground text-xs sm:text-sm">
Description Description
<Badge variant="outline" className="ml-2 text-[10px]"> <Badge variant="outline" className="ml-2 text-[10px]">
Optional Optional
@ -277,7 +277,7 @@ export function LLMConfigForm({
name="provider" name="provider"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>LLM Provider</FormLabel> <FormLabel className="text-xs sm:text-sm">LLM Provider</FormLabel>
<Select value={field.value} onValueChange={handleProviderChange}> <Select value={field.value} onValueChange={handleProviderChange}>
<FormControl> <FormControl>
<SelectTrigger className="transition-all focus:ring-violet-500/50"> <SelectTrigger className="transition-all focus:ring-violet-500/50">
@ -315,7 +315,7 @@ export function LLMConfigForm({
name="custom_provider" name="custom_provider"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel>Custom Provider Name</FormLabel> <FormLabel className="text-xs sm:text-sm">Custom Provider Name</FormLabel>
<FormControl> <FormControl>
<Input <Input
placeholder="my-custom-provider" placeholder="my-custom-provider"
@ -337,7 +337,7 @@ export function LLMConfigForm({
name="model_name" name="model_name"
render={({ field }) => ( render={({ field }) => (
<FormItem className="flex flex-col"> <FormItem className="flex flex-col">
<FormLabel>Model Name</FormLabel> <FormLabel className="text-xs sm:text-sm">Model Name</FormLabel>
<Popover open={modelComboboxOpen} onOpenChange={setModelComboboxOpen}> <Popover open={modelComboboxOpen} onOpenChange={setModelComboboxOpen}>
<PopoverTrigger asChild> <PopoverTrigger asChild>
<FormControl> <FormControl>
@ -410,7 +410,7 @@ export function LLMConfigForm({
</PopoverContent> </PopoverContent>
</Popover> </Popover>
{selectedProvider?.example && ( {selectedProvider?.example && (
<FormDescription className="text-xs"> <FormDescription className="text-[10px] sm:text-xs">
Example: {selectedProvider.example} Example: {selectedProvider.example}
</FormDescription> </FormDescription>
)} )}
@ -426,7 +426,7 @@ export function LLMConfigForm({
name="api_key" name="api_key"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="flex items-center gap-2"> <FormLabel className="flex items-center gap-2 text-xs sm:text-sm">
<Key className="h-3.5 w-3.5 text-amber-500" /> <Key className="h-3.5 w-3.5 text-amber-500" />
API Key API Key
</FormLabel> </FormLabel>
@ -438,7 +438,7 @@ export function LLMConfigForm({
/> />
</FormControl> </FormControl>
{watchProvider === "OLLAMA" && ( {watchProvider === "OLLAMA" && (
<FormDescription className="text-xs"> <FormDescription className="text-[10px] sm:text-xs">
Ollama doesn't require auth enter any value Ollama doesn't require auth enter any value
</FormDescription> </FormDescription>
)} )}
@ -452,7 +452,7 @@ export function LLMConfigForm({
name="api_base" name="api_base"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className="flex items-center gap-2"> <FormLabel className="flex items-center gap-2 text-xs sm:text-sm">
API Base URL API Base URL
{selectedProvider?.apiBase && ( {selectedProvider?.apiBase && (
<Badge variant="secondary" className="text-[10px]"> <Badge variant="secondary" className="text-[10px]">
@ -510,8 +510,8 @@ export function LLMConfigForm({
<> <>
<Separator /> <Separator />
<div className="space-y-4"> <div className="space-y-4">
<div className="flex items-center gap-2 text-sm font-medium text-muted-foreground"> <div className="flex items-center gap-2 text-xs sm:text-sm font-medium text-muted-foreground">
<Sparkles className="h-4 w-4" /> <Sparkles className="h-3.5 w-3.5 sm:h-4 sm:w-4" />
Advanced Parameters Advanced Parameters
</div> </div>
@ -542,19 +542,19 @@ export function LLMConfigForm({
)} )}
> >
{onCancel && ( {onCancel && (
<Button type="button" variant="outline" onClick={onCancel} disabled={isSubmitting}> <Button type="button" variant="outline" onClick={onCancel} disabled={isSubmitting} className="text-xs sm:text-sm h-9 sm:h-10">
Cancel Cancel
</Button> </Button>
)} )}
<Button type="submit" disabled={isSubmitting} className="gap-2 min-w-[160px]"> <Button type="submit" disabled={isSubmitting} className="gap-2 min-w-[140px] sm:min-w-[160px] text-xs sm:text-sm h-9 sm:h-10">
{isSubmitting ? ( {isSubmitting ? (
<> <>
<Loader2 className="h-4 w-4 animate-spin" /> <Loader2 className="h-3.5 w-3.5 sm:h-4 sm:w-4 animate-spin" />
{mode === "edit" ? "Updating..." : "Creating..."} {mode === "edit" ? "Updating..." : "Creating..."}
</> </>
) : ( ) : (
<> <>
{!compact && <Rocket className="h-4 w-4" />} {!compact && <Rocket className="h-3.5 w-3.5 sm:h-4 sm:w-4" />}
{submitLabel ?? (mode === "edit" ? "Update Configuration" : "Create Configuration")} {submitLabel ?? (mode === "edit" ? "Update Configuration" : "Create Configuration")}
</> </>
)} )}

View file

@ -81,7 +81,7 @@ export function ConnectorsTab({ searchSpaceId }: ConnectorsTabProps) {
className="w-full" className="w-full"
> >
<div className="flex items-center justify-between space-x-4 p-4"> <div className="flex items-center justify-between space-x-4 p-4">
<h3 className="text-xl font-semibold">{t(category.title)}</h3> <h3 className="text-lg sm:text-xl font-semibold">{t(category.title)}</h3>
<CollapsibleTrigger asChild> <CollapsibleTrigger asChild>
<Button variant="ghost" size="sm" className="w-9 p-0 hover:bg-muted"> <Button variant="ghost" size="sm" className="w-9 p-0 hover:bg-muted">
<motion.div <motion.div

View file

@ -217,7 +217,7 @@ export function DocumentUploadTab({ searchSpaceId }: DocumentUploadTabProps) {
> >
<Alert> <Alert>
<Info className="h-4 w-4" /> <Info className="h-4 w-4" />
<AlertDescription>{t("file_size_limit")}</AlertDescription> <AlertDescription className="text-xs sm:text-sm">{t("file_size_limit")}</AlertDescription>
</Alert> </Alert>
<Card className="relative overflow-hidden"> <Card className="relative overflow-hidden">
@ -249,7 +249,7 @@ export function DocumentUploadTab({ searchSpaceId }: DocumentUploadTabProps) {
> >
<Upload className="h-12 w-12 text-muted-foreground" /> <Upload className="h-12 w-12 text-muted-foreground" />
<div className="text-center"> <div className="text-center">
<p className="text-lg font-medium">{t("drag_drop")}</p> <p className="text-base sm:text-lg font-medium">{t("drag_drop")}</p>
<p className="text-sm text-muted-foreground mt-1">{t("or_browse")}</p> <p className="text-sm text-muted-foreground mt-1">{t("or_browse")}</p>
</div> </div>
</motion.div> </motion.div>
@ -284,8 +284,10 @@ export function DocumentUploadTab({ searchSpaceId }: DocumentUploadTabProps) {
<CardHeader> <CardHeader>
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<CardTitle>{t("selected_files", { count: files.length })}</CardTitle> <CardTitle className="text-lg sm:text-2xl">
<CardDescription> {t("selected_files", { count: files.length })}
</CardTitle>
<CardDescription className="text-xs sm:text-sm">
{t("total_size")}: {formatFileSize(getTotalFileSize())} {t("total_size")}: {formatFileSize(getTotalFileSize())}
</CardDescription> </CardDescription>
</div> </div>
@ -313,7 +315,9 @@ export function DocumentUploadTab({ searchSpaceId }: DocumentUploadTabProps) {
<div className="flex items-center gap-3 flex-1 min-w-0"> <div className="flex items-center gap-3 flex-1 min-w-0">
<FileType className="h-5 w-5 text-muted-foreground flex-shrink-0" /> <FileType className="h-5 w-5 text-muted-foreground flex-shrink-0" />
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<p className="font-medium truncate">{file.name}</p> <p className="text-sm sm:text-base font-medium truncate">
{file.name}
</p>
<div className="flex items-center gap-2 mt-1"> <div className="flex items-center gap-2 mt-1">
<Badge variant="secondary" className="text-xs"> <Badge variant="secondary" className="text-xs">
{formatFileSize(file.size)} {formatFileSize(file.size)}
@ -361,7 +365,7 @@ export function DocumentUploadTab({ searchSpaceId }: DocumentUploadTabProps) {
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
> >
<Button <Button
className="w-full py-6 text-base font-medium" className="w-full py-4 sm:py-6 text-sm sm:text-base font-medium"
onClick={handleUpload} onClick={handleUpload}
disabled={isUploading || files.length === 0} disabled={isUploading || files.length === 0}
> >

View file

@ -125,17 +125,19 @@ export function YouTubeTab({ searchSpaceId }: YouTubeTabProps) {
> >
<Card> <Card>
<CardHeader> <CardHeader>
<CardTitle className="flex items-center gap-2"> <CardTitle className="text-lg sm:text-2xl flex items-center gap-2">
<IconBrandYoutube className="h-5 w-5" /> <IconBrandYoutube className="h-5 w-5" />
{t("title")} {t("title")}
</CardTitle> </CardTitle>
<CardDescription>{t("subtitle")}</CardDescription> <CardDescription className="text-xs sm:text-sm">{t("subtitle")}</CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<div className="space-y-4"> <div className="space-y-4">
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="video-input">{t("label")}</Label> <Label htmlFor="video-input" className="text-sm sm:text-base">
{t("label")}
</Label>
<TagInput <TagInput
id="video-input" id="video-input"
tags={videoTags} tags={videoTags}
@ -212,14 +214,17 @@ export function YouTubeTab({ searchSpaceId }: YouTubeTabProps) {
<CardFooter className="flex justify-between"> <CardFooter className="flex justify-between">
<Button <Button
variant="outline" variant="outline"
size="sm"
onClick={() => router.push(`/dashboard/${searchSpaceId}/documents`)} onClick={() => router.push(`/dashboard/${searchSpaceId}/documents`)}
className="text-xs sm:text-sm"
> >
{t("cancel")} {t("cancel")}
</Button> </Button>
<Button <Button
onClick={handleSubmit} onClick={handleSubmit}
disabled={isSubmitting || videoTags.length === 0} disabled={isSubmitting || videoTags.length === 0}
className="relative overflow-hidden" size="sm"
className="relative overflow-hidden text-xs sm:text-sm"
> >
{isSubmitting ? ( {isSubmitting ? (
<> <>