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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
"@types/react-dom": "^19.2.1",
"@vitejs/plugin-react-swc": "^4.1.0",
"eslint": "^9.37.0",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-hooks": "^7.0.0",
"eslint-plugin-react-refresh": "^0.4.23",
"globals": "^16.4.0",
"knip": "^5.64.2",
Expand Down
41 changes: 35 additions & 6 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion src/components/data-table/bulk-actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ export function DataTableBulkActions<TData>({
useEffect(() => {
if (selectedCount > 0) {
const message = `${selectedCount} ${entityName}${selectedCount > 1 ? 's' : ''} selected. Bulk actions toolbar is available.`
setAnnouncement(message)

// Use queueMicrotask to defer state update and avoid cascading renders
queueMicrotask(() => {
setAnnouncement(message)
})

// Clear announcement after a delay
const timer = setTimeout(() => setAnnouncement(''), 3000)
Expand Down
21 changes: 10 additions & 11 deletions src/components/long-text.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from 'react'
import { useRef, useState } from 'react'
import { cn } from '@/lib/utils'
import {
Popover,
Expand Down Expand Up @@ -26,18 +26,17 @@ export function LongText({
const ref = useRef<HTMLDivElement>(null)
const [isOverflown, setIsOverflown] = useState(false)

useEffect(() => {
if (checkOverflow(ref.current)) {
setIsOverflown(true)
return
// Use ref callback to check overflow when element is mounted
const refCallback = (node: HTMLDivElement | null) => {
ref.current = node
if (node && checkOverflow(node)) {
queueMicrotask(() => setIsOverflown(true))
}

setIsOverflown(false)
}, [])
}

if (!isOverflown)
return (
<div ref={ref} className={cn('truncate', className)}>
<div ref={refCallback} className={cn('truncate', className)}>
{children}
</div>
)
Expand All @@ -48,7 +47,7 @@ export function LongText({
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<div ref={ref} className={cn('truncate', className)}>
<div ref={refCallback} className={cn('truncate', className)}>
{children}
</div>
</TooltipTrigger>
Expand All @@ -61,7 +60,7 @@ export function LongText({
<div className='sm:hidden'>
<Popover>
<PopoverTrigger asChild>
<div ref={ref} className={cn('truncate', className)}>
<div ref={refCallback} className={cn('truncate', className)}>
{children}
</div>
</PopoverTrigger>
Expand Down
16 changes: 8 additions & 8 deletions src/features/auth/sign-in/components/user-auth-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,19 +54,19 @@ export function UserAuthForm({
function onSubmit(data: z.infer<typeof formSchema>) {
setIsLoading(true)

// Mock successful authentication
const mockUser = {
accountNo: 'ACC001',
email: data.email,
role: ['user'],
exp: Date.now() + 24 * 60 * 60 * 1000, // 24 hours from now
}

toast.promise(sleep(2000), {
loading: 'Signing in...',
success: () => {
setIsLoading(false)

// Mock successful authentication with expiry computed at success time
const mockUser = {
accountNo: 'ACC001',
email: data.email,
role: ['user'],
exp: Date.now() + 24 * 60 * 60 * 1000, // 24 hours from now
}

// Set user and access token
auth.setUser(mockUser)
auth.setAccessToken('mock-access-token')
Expand Down
12 changes: 7 additions & 5 deletions src/features/chats/components/new-chat.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useState } from 'react'
import { useState } from 'react'
import { Check, X } from 'lucide-react'
import { showSubmittedData } from '@/lib/show-submitted-data'
import { Badge } from '@/components/ui/badge'
Expand Down Expand Up @@ -41,14 +41,16 @@ export function NewChat({ users, onOpenChange, open }: NewChatProps) {
setSelectedUsers(selectedUsers.filter((user) => user.id !== userId))
}

useEffect(() => {
if (!open) {
const handleOpenChange = (newOpen: boolean) => {
onOpenChange(newOpen)
// Reset selected users when dialog closes
if (!newOpen) {
setSelectedUsers([])
}
}, [open])
}

return (
<Dialog open={open} onOpenChange={onOpenChange}>
<Dialog open={open} onOpenChange={handleOpenChange}>
<DialogContent className='sm:max-w-[600px]'>
<DialogHeader>
<DialogTitle>New message</DialogTitle>
Expand Down
78 changes: 37 additions & 41 deletions src/features/dashboard/components/analytics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,47 +8,6 @@ import {
import { AnalyticsChart } from './analytics-chart'

export function Analytics() {
type Item = { name: string; value: number }

const SimpleBarList = ({
items,
valueFormatter,
barClass,
}: {
items: Item[]
valueFormatter: (n: number) => string
barClass: string
}) => {
const max = Math.max(...items.map((i) => i.value), 1)
return (
<ul className='space-y-3'>
{items.map((i) => {
const width = `${Math.round((i.value / max) * 100)}%`
return (
<li
key={i.name}
className='flex items-center justify-between gap-3'
>
<div className='min-w-0 flex-1'>
<div className='text-muted-foreground mb-1 truncate text-xs'>
{i.name}
</div>
<div className='bg-muted h-2.5 w-full rounded-full'>
<div
className={`h-2.5 rounded-full ${barClass}`}
style={{ width }}
/>
</div>
</div>
<div className='ps-2 text-xs font-medium tabular-nums'>
{valueFormatter(i.value)}
</div>
</li>
)
})}
</ul>
)
}
return (
<div className='space-y-4'>
<Card>
Expand Down Expand Up @@ -191,3 +150,40 @@ export function Analytics() {
</div>
)
}

function SimpleBarList({
items,
valueFormatter,
barClass,
}: {
items: { name: string; value: number }[]
valueFormatter: (n: number) => string
barClass: string
}) {
const max = Math.max(...items.map((i) => i.value), 1)
return (
<ul className='space-y-3'>
{items.map((i) => {
const width = `${Math.round((i.value / max) * 100)}%`
return (
<li key={i.name} className='flex items-center justify-between gap-3'>
<div className='min-w-0 flex-1'>
<div className='text-muted-foreground mb-1 truncate text-xs'>
{i.name}
</div>
<div className='bg-muted h-2.5 w-full rounded-full'>
<div
className={`h-2.5 rounded-full ${barClass}`}
style={{ width }}
/>
</div>
</div>
<div className='ps-2 text-xs font-medium tabular-nums'>
{valueFormatter(i.value)}
</div>
</li>
)
})}
</ul>
)
}