Make the task modal more similar to the share modal (#164)

This commit is contained in:
Matt Rubens 2025-07-01 09:53:47 -04:00 committed by GitHub
parent b9229528e9
commit 2d2096c58f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 59 additions and 14 deletions

View file

@ -13,7 +13,6 @@ const PlainTextLink = ({ children }: { children?: React.ReactNode }) => {
type MessagesProps = {
messages: Message[];
maxHeight?: string;
};
type SuggestionItem = string | { answer: string };
@ -40,7 +39,7 @@ const parseQuestionData = (text: string): QuestionData | null => {
return null;
};
export const Messages = ({ messages, maxHeight }: MessagesProps) => {
export const Messages = ({ messages }: MessagesProps) => {
const { containerRef, scrollToBottom, autoScrollToBottom, userHasScrolled } =
useAutoScroll<HTMLDivElement>({
enabled: true,
@ -79,7 +78,7 @@ export const Messages = ({ messages, maxHeight }: MessagesProps) => {
{/* Scrollable messages container */}
<div
ref={containerRef}
className={`space-y-6 pr-2 overflow-y-auto ${maxHeight || ''}`}
className="space-y-6 pr-2 overflow-y-auto"
style={{
scrollbarWidth: 'thin',
scrollbarColor: 'hsl(var(--border)) transparent',

View file

@ -1,5 +1,6 @@
import { useQuery } from '@tanstack/react-query';
import { useAuth } from '@clerk/nextjs';
import { useEffect } from 'react';
import type { TaskWithUser } from '@/actions/analytics';
import { getMessages } from '@/actions/analytics';
@ -7,7 +8,7 @@ import { canShareTask } from '@/actions/taskSharing';
import { useOrganizationSettings } from '@/hooks/useOrganizationSettings';
import { useRealtimePolling } from '@/hooks/useRealtimePolling';
import { QueryKey } from '@/types/react-query';
import { Dialog, DialogContentLarge } from '@/components/ui';
import { Dialog, DialogContentFullScreen } from '@/components/ui';
import { ShareButton } from '@/components/task-sharing/ShareButton';
import { TaskDetails } from '@/components/task-sharing/TaskDetails';
@ -51,15 +52,32 @@ export const TaskModal = ({ task, open, onClose }: TaskModalProps) => {
</>
);
// Handle ESC key to close modal
useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape' && open) {
onClose();
}
};
if (open) {
document.addEventListener('keydown', handleKeyDown);
}
return () => {
document.removeEventListener('keydown', handleKeyDown);
};
}, [open, onClose]);
return (
<Dialog open={open} onOpenChange={onClose}>
<DialogContentLarge>
<DialogContentFullScreen>
<TaskDetails
task={task}
messages={messages}
headerActions={headerActions}
/>
</DialogContentLarge>
</DialogContentFullScreen>
</Dialog>
);
};

View file

@ -51,7 +51,6 @@ export const SharedTaskView = ({
sharedBy={sharedBy}
sharedAt={sharedAt}
showSharedInfo={true}
limitMessagesHeight={false}
/>
);
};

View file

@ -22,7 +22,6 @@ type TaskDetailsProps = {
sharedAt?: Date;
showSharedInfo?: boolean;
headerActions?: React.ReactNode;
limitMessagesHeight?: boolean;
};
export const TaskDetails = ({
@ -32,12 +31,11 @@ export const TaskDetails = ({
sharedAt,
showSharedInfo = false,
headerActions,
limitMessagesHeight = true,
}: TaskDetailsProps) => {
const taskTitle = task.title || generateFallbackTitle(task);
return (
<div className="max-w-4xl mx-auto space-y-6">
<div className="space-y-6">
{/* Task Header */}
<Card>
<CardHeader>
@ -154,10 +152,7 @@ export const TaskDetails = ({
<CardTitle>Conversation</CardTitle>
</CardHeader>
<CardContent>
<Messages
messages={messages}
maxHeight={limitMessagesHeight ? 'max-h-[600px]' : undefined}
/>
<Messages messages={messages} />
</CardContent>
</Card>
) : (

View file

@ -147,11 +147,45 @@ function DialogDescription({
);
}
function DialogContentFullScreen({
className,
children,
...props
}: React.ComponentProps<typeof DialogPrimitive.Content>) {
return (
<DialogPortal data-slot="dialog-portal">
<DialogPrimitive.Overlay
data-slot="dialog-overlay"
className="fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0"
/>
<DialogPrimitive.Content
data-slot="dialog-content"
className={cn(
'fixed inset-0 z-50 bg-background 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 duration-200',
className,
)}
{...props}
>
<DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-6 right-6 z-10 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4">
<XIcon />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
<div className="h-full overflow-y-auto">
<div className="container mx-auto py-6 px-4 sm:px-6 lg:px-8">
{children}
</div>
</div>
</DialogPrimitive.Content>
</DialogPortal>
);
}
export {
Dialog,
DialogClose,
DialogContent,
DialogContentLarge,
DialogContentFullScreen,
DialogDescription,
DialogFooter,
DialogHeader,