Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
234 changes: 191 additions & 43 deletions package-lock.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
'use client';

import * as React from 'react';
import { formatDate, type DatePreset } from '@/lib/utils/date/format';
import { useLocale } from '@/lib/i18n/client';
import { type DatePreset } from '@/lib/utils/date/format';
import { useFormatDate } from '@/app/hooks/use-format-date';
import { cn } from '@/lib/utils/cn';

interface TableDateCellProps {
Expand Down Expand Up @@ -41,7 +41,7 @@ export const TableDateCell = React.memo(function TableDateCell({
emptyText = '—',
alignRight = false,
}: TableDateCellProps) {
const locale = useLocale();
const { formatDate } = useFormatDate();

if (date === null || date === undefined) {
return (
Expand All @@ -62,9 +62,9 @@ export const TableDateCell = React.memo(function TableDateCell({
? new Date(date)
: date;

const formatted = formatDate(dateObj, { preset, locale });
const formatted = formatDate(dateObj, preset);
// Use formatDate for title to ensure SSR/CSR consistency
const titleText = formatDate(dateObj, { preset: 'long', locale });
const titleText = formatDate(dateObj, 'long');

return (
<span
Expand Down
13 changes: 10 additions & 3 deletions services/platform/app/components/ui/data-table/data-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export interface DataTableProps<TData> {
isInitialLoading?: boolean;
/** Enable automatic loading on scroll (default: true) */
autoLoad?: boolean;
/** Distance from bottom to trigger load in px (default: 300) */
/** Distance from bottom to trigger load in px (default: 500) */
threshold?: number;
};
/** Sorting configuration from useDataTable hook */
Expand Down Expand Up @@ -207,6 +207,9 @@ export function DataTable<TData>({
},
);

// Ref to the scroll container for sticky layout (needed for IntersectionObserver root)
const scrollContainerRef = useRef<HTMLDivElement>(null);

// Track previous row count for animation on load more
const prevRowCountRef = useRef(0);
const [animatingRows, setAnimatingRows] = useState<Set<string>>(new Set());
Expand Down Expand Up @@ -237,8 +240,9 @@ export function DataTable<TData>({
onLoadMore: infiniteScroll?.onLoadMore ?? noop,
hasMore: infiniteScroll?.hasMore ?? false,
isLoading: infiniteScroll?.isLoadingMore ?? false,
threshold: infiniteScroll?.threshold ?? 300,
threshold: infiniteScroll?.threshold ?? 500,
enabled: !!(infiniteScroll && infiniteScroll.autoLoad !== false),
root: stickyLayout ? scrollContainerRef : undefined,
});

// Use controlled or internal state
Expand Down Expand Up @@ -607,7 +611,10 @@ export function DataTable<TData>({
{showFilteredEmptyState ? (
filteredEmptyContent
) : (
<div className="min-h-0 overflow-auto rounded-xl border border-border">
<div
ref={scrollContainerRef}
className="min-h-0 overflow-auto rounded-xl border border-border"
>
{tableContent}
{infiniteScrollContent}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import { ProductCard } from './product-card';
import { ApprovalDetail } from '../types/approval-detail';
import { Button } from '@/app/components/ui/primitives/button';
import { cn } from '@/lib/utils/cn';
import { formatDate as formatDateUtil } from '@/lib/utils/date/format';
import { useFormatDate } from '@/app/hooks/use-format-date';
import { useQuery } from 'convex/react';
import { api } from '@/convex/_generated/api';
import { useLocale, useT } from '@/lib/i18n/client';
import { useT } from '@/lib/i18n/client';
import { CustomerInfoDialog } from '@/app/features/customers/components/customer-info-dialog';

const RecommendationIcon = () => (
Expand Down Expand Up @@ -43,7 +43,7 @@ export function ApprovalDetailDialog({
removingProductId,
}: ApprovalDetailDialogProps) {
const { t } = useT('approvals');
const locale = useLocale();
const { formatDate } = useFormatDate();
const [customerInfoOpen, setCustomerInfoOpen] = useState(false);

const customer = useQuery(
Expand Down Expand Up @@ -75,13 +75,6 @@ export function ApprovalDetailDialog({
onRemoveRecommendation(approvalDetail._id, productId);
};

const formatDate = (timestamp: number) => {
return formatDateUtil(new Date(timestamp).toISOString(), {
preset: 'long',
locale,
});
};

const visibleProducts = sortedProducts.slice(0, 3);

const footer =
Expand Down Expand Up @@ -184,7 +177,7 @@ export function ApprovalDetailDialog({
{t('detail.createdAt')}
</div>
<div className="text-sm font-medium text-muted-foreground">
{formatDate(approvalDetail.createdAt)}
{formatDate(new Date(approvalDetail.createdAt), 'long')}
</div>
</HStack>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ import { useT } from '@/lib/i18n/client';
import { ApprovalDetailDialog } from './approval-detail-dialog';
import { ApprovalDetail } from '../types/approval-detail';
import { Button } from '@/app/components/ui/primitives/button';
import { formatDate } from '@/lib/utils/date/format';
import { useFormatDate } from '@/app/hooks/use-format-date';
import { useQuery } from 'convex/react';
import { useLocale } from '@/lib/i18n/client';
import { api } from '@/convex/_generated/api';
import type { Id } from '@/convex/_generated/dataModel';
import { useUpdateApprovalStatus } from '../hooks/use-update-approval-status';
Expand Down Expand Up @@ -58,15 +57,15 @@ function ApprovalsSkeleton({ status }: { status?: 'pending' | 'resolved' }) {
const columns =
status === 'resolved'
? [
{ header: t('columns.approvalRecipient') },
{ header: t('columns.approvalRecipient'), size: 256 },
{ header: t('columns.event'), size: 256 },
{ header: t('columns.action'), size: 256 },
{ header: t('columns.reviewer') },
{ header: t('columns.reviewedAt') },
{ header: t('columns.approved'), size: 100 },
]
: [
{ header: t('columns.approvalRecipient') },
{ header: t('columns.approvalRecipient'), size: 256 },
{ header: t('columns.event'), size: 256 },
{ header: t('columns.action'), size: 256 },
{ header: t('columns.confidence'), size: 100 },
Expand All @@ -90,7 +89,7 @@ export function ApprovalsClient({
search,
}: ApprovalsClientProps) {
const { t } = useT('approvals');
const locale = useLocale();
const { formatDate } = useFormatDate();

const getApprovalTypeLabel = useCallback(
(resourceType: string): string => {
Expand Down Expand Up @@ -723,10 +722,7 @@ export function ApprovalsClient({
cell: ({ row }) => (
<span className="text-sm text-right block">
{row.original.reviewedAt
? formatDate(new Date(row.original.reviewedAt).toISOString(), {
preset: 'short',
locale,
})
? formatDate(new Date(row.original.reviewedAt), 'short')
: ''}
</span>
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { Button } from '@/app/components/ui/primitives/button';
import { Stack, HStack } from '@/app/components/ui/layout/layout';
import { X } from 'lucide-react';
import { RecommendedProduct, PreviousPurchase } from '../types/approval-detail';
import { formatDate } from '@/lib/utils/date/format';
import { useLocale, useT } from '@/lib/i18n/client';
import { useFormatDate } from '@/app/hooks/use-format-date';
import { useT } from '@/lib/i18n/client';

interface ProductCardProps {
product?: RecommendedProduct;
Expand All @@ -26,7 +26,7 @@ export function ProductCard({
isRemoving,
canRemove,
}: ProductCardProps) {
const locale = useLocale();
const { formatDate } = useFormatDate();
const { t } = useT('approvals');

if (type === 'recommended' && product) {
Expand Down Expand Up @@ -109,7 +109,7 @@ export function ProductCard({
</h4>
{purchase.purchaseDate && (
<p className="text-xs text-muted-foreground">
{formatDate(purchase.purchaseDate, { locale })}
{formatDate(purchase.purchaseDate)}
</p>
)}
</Stack>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { ImagePreviewDialog } from '@/app/features/chat/components/message-bubbl
import { FileUpload } from '@/app/components/ui/forms/file-upload';
import { useConvexFileUpload } from '@/app/features/chat/hooks/use-convex-file-upload';
import { useT } from '@/lib/i18n/client';
import { TEXT_FILE_ACCEPT } from '@/lib/utils/text-file-types';

// Module-level guard to prevent duplicate sends (survives component remounts)
const recentSends = new Map<string, number>();
Expand Down Expand Up @@ -262,7 +263,6 @@ function AutomationAssistantContent({
onClearChatStateChange,
}: AutomationAssistantProps) {
const { t } = useT('automations');
const { t: tCommon } = useT('common');

const {
attachments,
Expand Down Expand Up @@ -776,7 +776,7 @@ function AutomationAssistantContent({
ref={fileInputRef}
type="file"
multiple
accept="image/*,.pdf,.doc,.docx,.ppt,.pptx,.txt"
accept={TEXT_FILE_ACCEPT}
onChange={handleFileInputChange}
style={{ display: 'none' }}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import { HStack } from '@/app/components/ui/layout/layout';
import { Button } from '@/app/components/ui/primitives/button';
import { Badge } from '@/app/components/ui/feedback/badge';
import { JsonViewer } from '@/app/components/ui/data-display/json-viewer';
import { useT, useLocale } from '@/lib/i18n/client';
import { useLocale } from '@/app/hooks/use-locale';
import { useT } from '@/lib/i18n/client';
import { formatDuration } from '@/lib/utils/format/number';
import { useExecutionsTableConfig } from './use-executions-table-config';
import { ExecutionsTableSkeleton } from './executions-table-skeleton';
Expand Down Expand Up @@ -115,7 +116,7 @@ export function ExecutionsClient({
}: ExecutionsClientProps) {
const navigate = useNavigate();
const [copiedId, setCopiedId] = useState<string | null>(null);
const locale = useLocale();
const { locale } = useLocale();
const { t: tCommon } = useT('common');
const { t: tTables } = useT('tables');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export function ChatHeader({ organizationId }: ChatHeaderProps) {
</SheetContent>
</Sheet>

<div className="hidden md:flex items-center gap-1 px-5 py-2 border-b border-border">
<div className="hidden md:flex items-center gap-1 px-5 h-13 border-b border-border">
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { useQuery } from 'convex/react';
import { api } from '@/convex/_generated/api';
import { cn } from '@/lib/utils/cn';
import { useT } from '@/lib/i18n/client';
import { Input } from '@/app/components/ui/forms/input';
import { Stack } from '@/app/components/ui/layout/layout';
import { useToast } from '@/app/hooks/use-toast';
import { ChatActions } from './chat-actions';
Expand Down Expand Up @@ -190,14 +189,14 @@ export function ChatHistorySidebar({
key={chat._id}
className={cn(
'group relative flex items-center gap-2 px-2 py-1.5 rounded-md text-sm transition-colors',
!isEditing && 'hover:bg-accent hover:text-accent-foreground',
!isEditing && 'cursor-pointer hover:bg-accent hover:text-accent-foreground',
currentThreadId === chat._id &&
!isEditing &&
'bg-accent text-accent-foreground',
)}
>
{isEditing ? (
<Input
<input
ref={inputRef}
value={editValue}
onChange={(e) => setEditValue(e.target.value)}
Expand All @@ -212,7 +211,7 @@ export function ChatHistorySidebar({
}}
onBlur={() => handleInputBlur(chat._id)}
aria-label={t('history.renameChat')}
className="w-full text-sm leading-snug min-h-[20px] px-0 py-0 border-0 ring-1 ring-primary rounded-sm focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-0 shadow-none bg-transparent"
className="flex-1 min-w-0 text-sm leading-snug min-h-[20px] bg-transparent outline-none ring-1 ring-primary rounded-sm focus-visible:ring-2 focus-visible:ring-primary px-1"
/>
) : (
<>
Expand Down
11 changes: 9 additions & 2 deletions services/platform/app/features/chat/components/chat-input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ import {
useConvexFileUpload,
type FileAttachment,
} from '../hooks/use-convex-file-upload';
import {
isTextBasedFile,
getFileExtensionLower,
TEXT_FILE_ACCEPT,
} from '@/lib/utils/text-file-types';
import { ImagePreviewDialog } from './message-bubble';

interface ChatInputProps extends Omit<
Expand Down Expand Up @@ -131,7 +136,7 @@ export function ChatInput({
ref={fileInputRef}
type="file"
multiple
accept="image/*,.pdf,.doc,.docx,.txt,.ppt,.pptx"
accept={TEXT_FILE_ACCEPT}
onChange={handleFileInputChange}
style={{ display: 'none' }}
/>
Expand Down Expand Up @@ -198,7 +203,9 @@ export function ChatInput({
? tChat('fileTypes.pptx')
: attachment.fileType === 'text/plain'
? tChat('fileTypes.txt')
: tChat('fileTypes.file')}
: isTextBasedFile(attachment.fileName, attachment.fileType)
? getFileExtensionLower(attachment.fileName).toUpperCase() || tChat('fileTypes.txt')
: tChat('fileTypes.file')}
</div>
</div>
<button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { api } from '@/convex/_generated/api';
import { cn } from '@/lib/utils/cn';
import { useT } from '@/lib/i18n/client';
import { useDebounce } from '@/app/hooks/use-debounce';
import { useDateFormat } from '@/app/hooks/use-date-format';
import { useFormatDate } from '@/app/hooks/use-format-date';
import { Dialog } from '@/app/components/ui/dialog/dialog';
import { Input } from '@/app/components/ui/forms/input';

Expand All @@ -26,7 +26,7 @@ export function ChatSearchDialog({
}: ChatSearchDialogProps) {
const { t } = useT('dialogs');
const { t: tCommon } = useT('common');
const { formatDateSmart } = useDateFormat();
const { formatDateSmart } = useFormatDate();
const navigate = useNavigate();
const [query, setQuery] = useState('');
const [selectedIndex, setSelectedIndex] = useState(-1);
Expand Down Expand Up @@ -123,7 +123,7 @@ export function ChatSearchDialog({
}
>
<div className="h-[13.75rem] overflow-y-auto p-3">
{chats.length === 0 ? (
{threadsData !== undefined && chats.length === 0 ? (
<div className="text-sm text-muted-foreground px-4 py-6 size-full flex items-center justify-center">
{t('searchChat.noResults')}
</div>
Expand All @@ -134,7 +134,7 @@ export function ChatSearchDialog({
<button
type="button"
className={cn(
'w-full text-left flex items-start gap-3 p-3 hover:bg-muted transition-colors rounded-lg',
'w-full text-left flex items-start gap-3 p-3 hover:bg-muted transition-colors rounded-lg cursor-pointer',
idx === selectedIndex && 'bg-muted',
)}
onMouseEnter={() => setSelectedIndex(idx)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
Send,
} from 'lucide-react';
import { cn } from '@/lib/utils/cn';
import { useFormatDate } from '@/app/hooks/use-format-date';

interface HumanInputRequestCardProps {
approvalId: Id<'approvals'>;
Expand All @@ -35,6 +36,7 @@ function HumanInputRequestCardComponent({
className,
onResponseSubmitted,
}: HumanInputRequestCardProps) {
const { formatDate } = useFormatDate();
const [isSubmitting, setIsSubmitting] = useState(false);
const [error, setError] = useState<string | null>(null);
const [textValue, setTextValue] = useState('');
Expand Down Expand Up @@ -238,7 +240,7 @@ function HumanInputRequestCardComponent({
<div className="text-sm font-medium">{displayValue}</div>
<div className="text-xs text-muted-foreground">
Responded by {respondedBy} at{' '}
{new Date(timestamp).toLocaleString()}
{formatDate(new Date(timestamp), 'long')}
</div>
</div>
);
Expand Down
Loading