add notification to admin dash if no stats generated yet (#346)

* add notification if no stats generated yet

* Update main.wasp

* fix app_diff

* Update AnalyticsDashboardPage.tsx
This commit is contained in:
vincanger
2024-12-17 17:02:38 +01:00
committed by GitHub
parent f77e029625
commit a0de7e9112
5 changed files with 64 additions and 50 deletions

View File

@@ -25,8 +25,10 @@
+ "prettier-plugin-tailwindcss": "0.5.11", + "prettier-plugin-tailwindcss": "0.5.11",
+ "react": "^18.2.0", + "react": "^18.2.0",
+ "react-apexcharts": "1.4.1", + "react-apexcharts": "1.4.1",
+ "react-dom": "^18.2.0",
+ "react-hot-toast": "^2.4.1", + "react-hot-toast": "^2.4.1",
+ "react-icons": "4.11.0", + "react-icons": "4.11.0",
+ "react-router-dom": "^6.26.2",
+ "stripe": "11.15.0", + "stripe": "11.15.0",
+ "tailwind-merge": "^2.2.1", + "tailwind-merge": "^2.2.1",
+ "vanilla-cookieconsent": "^3.0.1", + "vanilla-cookieconsent": "^3.0.1",
@@ -9013,7 +9015,6 @@
+ "version": "18.3.1", + "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
+ "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
+ "peer": true,
+ "dependencies": { + "dependencies": {
+ "loose-envify": "^1.1.0", + "loose-envify": "^1.1.0",
+ "scheduler": "^0.23.2" + "scheduler": "^0.23.2"
@@ -9327,7 +9328,6 @@
+ "version": "0.23.2", + "version": "0.23.2",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
+ "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
+ "peer": true,
+ "dependencies": { + "dependencies": {
+ "loose-envify": "^1.1.0" + "loose-envify": "^1.1.0"
+ } + }

View File

@@ -7,17 +7,12 @@
import { useQuery, getDailyStats } from 'wasp/client/operations'; import { useQuery, getDailyStats } from 'wasp/client/operations';
import TotalSignupsCard from './TotalSignupsCard'; import TotalSignupsCard from './TotalSignupsCard';
import TotalPageViewsCard from './TotalPageViewsCard'; import TotalPageViewsCard from './TotalPageViewsCard';
@@ -7,21 +9,68 @@ @@ -10,12 +12,54 @@
import RevenueAndProfitChart from './RevenueAndProfitChart'; import { useRedirectHomeUnlessUserIsAdmin } from '../../useRedirectHomeUnlessUserIsAdmin';
import SourcesTable from './SourcesTable';
import DefaultLayout from '../../layout/DefaultLayout';
-import { useRedirectHomeUnlessUserIsAdmin } from '../../useRedirectHomeUnlessUserIsAdmin'
+import { useRedirectHomeUnlessUserIsAdmin } from '../../useRedirectHomeUnlessUserIsAdmin';
const Dashboard = ({ user }: { user: AuthUser }) => { const Dashboard = ({ user }: { user: AuthUser }) => {
- useRedirectHomeUnlessUserIsAdmin({ user })
+ const [isDemoInfoVisible, setIsDemoInfoVisible] = useState(false); + const [isDemoInfoVisible, setIsDemoInfoVisible] = useState(false);
+ useRedirectHomeUnlessUserIsAdmin({ user }); useRedirectHomeUnlessUserIsAdmin({ user });
const { data: stats, isLoading, error } = useQuery(getDailyStats); const { data: stats, isLoading, error } = useQuery(getDailyStats);
@@ -48,7 +43,6 @@
+ +
return ( return (
<DefaultLayout user={user}> <DefaultLayout user={user}>
+ {/* Floating Demo Announcement */}
+ {isDemoInfoVisible && ( + {isDemoInfoVisible && (
+ <div className='fixed z-999 bottom-0 mb-2 left-1/2 -translate-x-1/2 lg:mb-4 bg-gray-700 rounded-full px-3.5 py-2 text-sm text-white duration-300 ease-in-out hover:bg-gray-800 focus-visible:outline focus-visible:outline-2 focus-visible:outline-indigo-600'> + <div className='fixed z-999 bottom-0 mb-2 left-1/2 -translate-x-1/2 lg:mb-4 bg-gray-700 rounded-full px-3.5 py-2 text-sm text-white duration-300 ease-in-out hover:bg-gray-800 focus-visible:outline focus-visible:outline-2 focus-visible:outline-indigo-600'>
+ <div className='px-4 flex flex-row gap-2 items-center my-1'> + <div className='px-4 flex flex-row gap-2 items-center my-1'>
@@ -65,26 +59,15 @@
+ </div> + </div>
+ </div> + </div>
+ )} + )}
<div className='grid grid-cols-1 gap-4 md:grid-cols-2 md:gap-6 xl:grid-cols-4 2xl:gap-7.5'> <div className='relative'>
<TotalPageViewsCard <div className={`${!stats ? 'opacity-25' : ''}`}>
totalPageViews={stats?.dailyStats.totalViews} <div className='grid grid-cols-1 gap-4 md:grid-cols-2 md:gap-6 xl:grid-cols-4 2xl:gap-7.5'>
prevDayViewsChangePercent={stats?.dailyStats.prevDayViewsChangePercent} @@ -36,7 +80,7 @@
/> <RevenueAndProfitChart weeklyStats={stats?.weeklyStats} isLoading={isLoading} />
- <TotalRevenueCard dailyStats={stats?.dailyStats} weeklyStats={stats?.weeklyStats} isLoading={isLoading} />
+ <TotalRevenueCard
+ dailyStats={stats?.dailyStats}
+ weeklyStats={stats?.weeklyStats}
+ isLoading={isLoading}
+ />
<TotalPayingUsersCard dailyStats={stats?.dailyStats} isLoading={isLoading} />
<TotalSignupsCard dailyStats={stats?.dailyStats} isLoading={isLoading} />
</div>
@@ -30,7 +79,7 @@
<RevenueAndProfitChart weeklyStats={stats?.weeklyStats} isLoading={isLoading} />
<div className='col-span-12 xl:col-span-8'> <div className='col-span-12 xl:col-span-8'>
- <SourcesTable sources={stats?.dailyStats?.sources} /> - <SourcesTable sources={stats?.dailyStats?.sources} />
+ <SourcesTable sources={sortedSources} /> + <SourcesTable sources={sortedSources} />
</div>
</div>
</div> </div>
</div>
</DefaultLayout>

View File

@@ -19,7 +19,13 @@
</WaspRouterLink> </WaspRouterLink>
</div> </div>
<div className='flex lg:hidden'> <div className='flex lg:hidden'>
@@ -61,8 +60,8 @@ @@ -56,13 +55,13 @@
</div>
<div className='hidden lg:flex lg:gap-x-12'>{renderNavigationItems(navigationItems)}</div>
<div className='hidden lg:flex lg:flex-1 gap-3 justify-end items-center'>
- <ul className='flex justify-center items-center gap-2 sm:gap-4'>
+ <ul className='ml-4 flex justify-center items-center gap-2 sm:gap-4'>
<DarkModeSwitcher />
</ul> </ul>
{isUserLoading ? null : !user ? ( {isUserLoading ? null : !user ? (
<WaspRouterLink to={routes.LoginRoute.to} className='text-sm font-semibold leading-6 ml-3'> <WaspRouterLink to={routes.LoginRoute.to} className='text-sm font-semibold leading-6 ml-3'>

View File

@@ -7,31 +7,55 @@ import TotalRevenueCard from './TotalRevenueCard';
import RevenueAndProfitChart from './RevenueAndProfitChart'; import RevenueAndProfitChart from './RevenueAndProfitChart';
import SourcesTable from './SourcesTable'; import SourcesTable from './SourcesTable';
import DefaultLayout from '../../layout/DefaultLayout'; import DefaultLayout from '../../layout/DefaultLayout';
import { useRedirectHomeUnlessUserIsAdmin } from '../../useRedirectHomeUnlessUserIsAdmin' import { useRedirectHomeUnlessUserIsAdmin } from '../../useRedirectHomeUnlessUserIsAdmin';
import { cn } from '../../../client/cn';
const Dashboard = ({ user }: { user: AuthUser }) => { const Dashboard = ({ user }: { user: AuthUser }) => {
useRedirectHomeUnlessUserIsAdmin({ user }) useRedirectHomeUnlessUserIsAdmin({ user });
const { data: stats, isLoading, error } = useQuery(getDailyStats); const { data: stats, isLoading, error } = useQuery(getDailyStats);
return ( return (
<DefaultLayout user={user}> <DefaultLayout user={user}>
<div className='grid grid-cols-1 gap-4 md:grid-cols-2 md:gap-6 xl:grid-cols-4 2xl:gap-7.5'> <div className='relative'>
<TotalPageViewsCard <div className={cn({
totalPageViews={stats?.dailyStats.totalViews} 'opacity-25': !stats,
prevDayViewsChangePercent={stats?.dailyStats.prevDayViewsChangePercent} })}>
/> <div className='grid grid-cols-1 gap-4 md:grid-cols-2 md:gap-6 xl:grid-cols-4 2xl:gap-7.5'>
<TotalRevenueCard dailyStats={stats?.dailyStats} weeklyStats={stats?.weeklyStats} isLoading={isLoading} /> <TotalPageViewsCard
<TotalPayingUsersCard dailyStats={stats?.dailyStats} isLoading={isLoading} /> totalPageViews={stats?.dailyStats.totalViews}
<TotalSignupsCard dailyStats={stats?.dailyStats} isLoading={isLoading} /> prevDayViewsChangePercent={stats?.dailyStats.prevDayViewsChangePercent}
</div> />
<TotalRevenueCard
dailyStats={stats?.dailyStats}
weeklyStats={stats?.weeklyStats}
isLoading={isLoading}
/>
<TotalPayingUsersCard dailyStats={stats?.dailyStats} isLoading={isLoading} />
<TotalSignupsCard dailyStats={stats?.dailyStats} isLoading={isLoading} />
</div>
<div className='mt-4 grid grid-cols-12 gap-4 md:mt-6 md:gap-6 2xl:mt-7.5 2xl:gap-7.5'> <div className='mt-4 grid grid-cols-12 gap-4 md:mt-6 md:gap-6 2xl:mt-7.5 2xl:gap-7.5'>
<RevenueAndProfitChart weeklyStats={stats?.weeklyStats} isLoading={isLoading} /> <RevenueAndProfitChart weeklyStats={stats?.weeklyStats} isLoading={isLoading} />
<div className='col-span-12 xl:col-span-8'> <div className='col-span-12 xl:col-span-8'>
<SourcesTable sources={stats?.dailyStats?.sources} /> <SourcesTable sources={stats?.dailyStats?.sources} />
</div>
</div>
</div> </div>
{!stats && (
<div className='absolute inset-0 flex items-start justify-center bg-white/50 dark:bg-boxdark-2/50'>
<div className='rounded-lg bg-white p-8 shadow-lg dark:bg-boxdark'>
<p className='text-2xl font-bold text-boxdark dark:text-white'>
No daily stats generated yet
</p>
<p className='mt-2 text-sm text-bodydark2'>
Stats will appear here once the daily stats job has run
</p>
</div>
</div>
)}
</div> </div>
</DefaultLayout> </DefaultLayout>
); );

View File

@@ -11,7 +11,7 @@ type DailyStatsValues = {
weeklyStats: DailyStatsWithSources[]; weeklyStats: DailyStatsWithSources[];
}; };
export const getDailyStats: GetDailyStats<void, DailyStatsValues> = async (_args, context) => { export const getDailyStats: GetDailyStats<void, DailyStatsValues | undefined> = async (_args, context) => {
if (!context.user?.isAdmin) { if (!context.user?.isAdmin) {
throw new HttpError(401); throw new HttpError(401);
} }
@@ -24,7 +24,8 @@ export const getDailyStats: GetDailyStats<void, DailyStatsValues> = async (_args
}, },
}); });
if (!dailyStats) { if (!dailyStats) {
throw new HttpError(204, 'No daily stats generated yet.'); console.log('\x1b[34mNote: No daily stats have been generated by the dailyStatsJob yet. \x1b[0m');
return undefined;
} }
const weeklyStats = await context.entities.DailyStats.findMany({ const weeklyStats = await context.entities.DailyStats.findMany({