Skip to content

Commit

Permalink
Merge pull request #358 from kruzhambus/skeleton-loader
Browse files Browse the repository at this point in the history
Add skeleton loader for dashboard
  • Loading branch information
Blaumaus authored Dec 16, 2024
2 parents 5502ed3 + 812e93d commit a0c47e2
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 35 deletions.
9 changes: 4 additions & 5 deletions web/app/pages/Dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { XCircleIcon } from '@heroicons/react/24/solid'

import Modal from '~/ui/Modal'
import { withAuthentication, auth } from '~/hoc/protected'
import Loader from '~/ui/Loader'
import routes from '~/utils/routes'
import { isSelfhosted, ENTRIES_PER_PAGE_DASHBOARD, LIVE_VISITORS_UPDATE_INTERVAL } from '~/lib/constants'
import EventsRunningOutBanner from '~/components/EventsRunningOutBanner'
Expand All @@ -20,7 +19,7 @@ import useDebounce from '~/hooks/useDebounce'
import Pagination from '~/ui/Pagination'
import { useSelector } from 'react-redux'
import { StateType } from '~/lib/store'
import { ProjectCard } from './ProjectCard'
import { ProjectCard, ProjectCardSkeleton } from './ProjectCard'
import { NoProjects } from './NoProjects'
import { AddProject } from './AddProject'
import { Overall, Project } from '~/lib/models/Project'
Expand Down Expand Up @@ -250,13 +249,13 @@ const Dashboard = () => {
)}
{isLoading || isLoading === null ? (
<div className='min-h-min-footer bg-gray-50 dark:bg-slate-900'>
<Loader />
<ProjectCardSkeleton />
</div>
) : (
<ClientOnly
fallback={
<div className='min-h-min-footer bg-gray-50 dark:bg-slate-900'>
<Loader />
<ProjectCardSkeleton />
</div>
}
>
Expand All @@ -270,7 +269,7 @@ const Dashboard = () => {
<ProjectCard
key={project.id}
project={project}
live={liveStats[project.id] ?? 'N/A'}
live={liveStats[project.id] ?? null}
overallStats={overallStats[project.id]}
/>
))}
Expand Down
95 changes: 65 additions & 30 deletions web/app/pages/Dashboard/ProjectCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import _size from 'lodash/size'
import _isNumber from 'lodash/isNumber'
import _replace from 'lodash/replace'
import _find from 'lodash/find'
import _map from 'lodash/map'
import { useTranslation } from 'react-i18next'
import { AdjustmentsVerticalIcon } from '@heroicons/react/24/outline'
import { ChevronUpIcon, ChevronDownIcon } from '@heroicons/react/20/solid'
Expand All @@ -22,16 +23,17 @@ import { useSelector } from 'react-redux'
import { StateType, useAppDispatch } from '~/lib/store'
import { authActions } from '~/lib/reducers/auth'
import { SquareArrowOutUpRightIcon } from 'lucide-react'
import Spin from '~/ui/icons/Spin'

interface ProjectCardProps {
live?: string | number
live?: string | number | null
overallStats?: OverallObject
project: Project
}

interface MiniCardProps {
labelTKey: string
total?: number | string
total?: number | string | null
percChange?: number
}

Expand All @@ -44,34 +46,40 @@ const MiniCard = ({ labelTKey, total = 0, percChange }: MiniCardProps) => {
<p className='text-sm text-gray-500 dark:text-gray-300'>{t(labelTKey)}</p>

<div className='flex font-bold'>
<p className='text-xl text-gray-700 dark:text-gray-100'>{_isNumber(total) ? nFormatter(total) : total}</p>
{_isNumber(percChange) && (
<p
className={cx('flex items-center text-xs', {
'text-green-600': statsDidGrowUp,
'text-red-600': !statsDidGrowUp,
})}
>
{statsDidGrowUp ? (
<>
<ChevronUpIcon className='h-4 w-4 flex-shrink-0 self-center text-green-500' />
<span className='sr-only'>{t('dashboard.inc')}</span>
</>
) : (
<>
<ChevronDownIcon className='h-4 w-4 flex-shrink-0 self-center text-red-500' />
<span className='sr-only'>{t('dashboard.dec')}</span>
</>
{total === null ? (
<Spin className='!ml-0 mt-2' />
) : (
<>
<p className='text-xl text-gray-700 dark:text-gray-100'>{_isNumber(total) ? nFormatter(total) : total}</p>
{_isNumber(percChange) && (
<p
className={cx('flex items-center text-xs', {
'text-green-600': statsDidGrowUp,
'text-red-600': !statsDidGrowUp,
})}
>
{statsDidGrowUp ? (
<>
<ChevronUpIcon className='h-4 w-4 flex-shrink-0 self-center text-green-500' />
<span className='sr-only'>{t('dashboard.inc')}</span>
</>
) : (
<>
<ChevronDownIcon className='h-4 w-4 flex-shrink-0 self-center text-red-500' />
<span className='sr-only'>{t('dashboard.dec')}</span>
</>
)}
{nFormatter(percChange)}%
</p>
)}
{nFormatter(percChange)}%
</p>
</>
)}
</div>
</div>
)
}

export const ProjectCard = ({ live = 'N/A', project, overallStats }: ProjectCardProps) => {
export const ProjectCard = ({ live = null, project, overallStats }: ProjectCardProps) => {
const { t } = useTranslation('common')
const [showInviteModal, setShowInviteModal] = useState(false)

Expand Down Expand Up @@ -211,13 +219,11 @@ export const ProjectCard = ({ live = 'N/A', project, overallStats }: ProjectCard
)}
</div>
<div className='mt-4 flex flex-shrink-0 gap-5'>
{overallStats ? (
<MiniCard
labelTKey={project.isCaptchaProject ? 'dashboard.captchaEvents' : 'dashboard.pageviews'}
total={overallStats.current.all}
percChange={calculateRelativePercentage(overallStats.previous.all, overallStats.current.all)}
/>
) : null}
<MiniCard
labelTKey={project.isCaptchaProject ? 'dashboard.captchaEvents' : 'dashboard.pageviews'}
total={overallStats?.current.all ?? null}
percChange={calculateRelativePercentage(overallStats?.previous.all ?? 0, overallStats?.current.all ?? 0)}
/>
{project.isAnalyticsProject && <MiniCard labelTKey='dashboard.liveVisitors' total={live} />}
</div>
</div>
Expand All @@ -241,3 +247,32 @@ export const ProjectCard = ({ live = 'N/A', project, overallStats }: ProjectCard
</Link>
)
}

export const ProjectCardSkeleton = () => {
return (
<div className='grid grid-cols-1 gap-x-6 gap-y-3 lg:grid-cols-3 lg:gap-y-6'>
{_map(Array(12), () => (
<div className='min-h-[153.1px] animate-pulse cursor-wait overflow-hidden rounded-xl border border-gray-200 bg-gray-50 dark:border-slate-800/25 dark:bg-slate-800'>
<div className='px-4 py-4'>
<div className='flex items-center justify-between'>
<div className='h-6 w-3/4 rounded bg-gray-200 dark:bg-slate-700'></div>
<div className='flex items-center gap-2'>
<div className='h-6 w-6 rounded-[3px] bg-gray-200 dark:bg-slate-700'></div>
<div className='h-6 w-6 rounded-[3px] bg-gray-200 dark:bg-slate-700'></div>
</div>
</div>
<div className='mt-1 flex flex-shrink-0 flex-wrap gap-2'>
<div className='h-4 w-16 rounded bg-gray-200 dark:bg-slate-700'></div>
<div className='h-4 w-16 rounded bg-gray-200 dark:bg-slate-700'></div>
<div className='h-4 w-16 rounded bg-gray-200 dark:bg-slate-700'></div>
</div>
<div className='mt-8 flex flex-shrink-0 gap-5'>
<div className='h-10 w-24 rounded bg-gray-200 dark:bg-slate-700'></div>
<div className='h-10 w-24 rounded bg-gray-200 dark:bg-slate-700'></div>
</div>
</div>
</div>
))}
</div>
)
}

0 comments on commit a0c47e2

Please sign in to comment.