Stats + removing FC
This commit is contained in:
parent
1c86864b8d
commit
48d20331fc
14 changed files with 162 additions and 94 deletions
|
@ -1,7 +1,7 @@
|
|||
import { GiTechnoHeart } from "react-icons/gi";
|
||||
import packagejson from "../../package.json";
|
||||
|
||||
const Footer: React.FC = () => {
|
||||
const Footer = () => {
|
||||
return (
|
||||
<footer className="footer footer-center h-12 p-2 bg-base-100 text-base-content">
|
||||
<div>
|
||||
|
|
|
@ -8,7 +8,7 @@ interface NavbarProps {
|
|||
title: string;
|
||||
}
|
||||
|
||||
const Navbar: React.FC<NavbarProps> = ({ title }) => {
|
||||
const Navbar = ({ title }: NavbarProps) => {
|
||||
const { data: sessionData, status: sessionStatus } = useSession();
|
||||
const router = useRouter();
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import { IoEnterOutline, IoTrashBinOutline } from "react-icons/io5";
|
|||
import { env } from "~/env.mjs";
|
||||
import { api } from "~/utils/api";
|
||||
|
||||
const RoomList: React.FC = () => {
|
||||
const RoomList = () => {
|
||||
const { data: sessionData } = useSession();
|
||||
|
||||
configureAbly({
|
||||
|
|
83
src/components/Stats.tsx
Normal file
83
src/components/Stats.tsx
Normal file
|
@ -0,0 +1,83 @@
|
|||
import { configureAbly, useChannel } from "@ably-labs/react-hooks";
|
||||
import { env } from "~/env.mjs";
|
||||
import { api } from "~/utils/api";
|
||||
|
||||
const Stats = () => {
|
||||
configureAbly({
|
||||
key: env.NEXT_PUBLIC_ABLY_PUBLIC_KEY,
|
||||
recover: (_, cb) => {
|
||||
cb(true);
|
||||
},
|
||||
});
|
||||
|
||||
const [] = useChannel(
|
||||
`${env.NEXT_PUBLIC_APP_ENV}-stats`,
|
||||
() => void refetchData()
|
||||
);
|
||||
|
||||
const {
|
||||
data: usersCount,
|
||||
isLoading: usersCountLoading,
|
||||
isFetching: usersCountFetching,
|
||||
refetch: refetchUsersCount,
|
||||
} = api.rest.userCount.useQuery();
|
||||
const {
|
||||
data: roomsCount,
|
||||
isLoading: roomsCountLoading,
|
||||
isFetching: roomsCountFetching,
|
||||
refetch: refetchRoomsCount,
|
||||
} = api.rest.roomCount.useQuery();
|
||||
const {
|
||||
data: votesCount,
|
||||
isLoading: votesCountLoading,
|
||||
isFetching: votesCountFetching,
|
||||
refetch: refetchVotesCount,
|
||||
} = api.rest.voteCount.useQuery();
|
||||
|
||||
const refetchData = async () => {
|
||||
await Promise.all([
|
||||
refetchUsersCount(),
|
||||
refetchRoomsCount(),
|
||||
refetchVotesCount(),
|
||||
]);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="stats stats-horizontal shadow bg-neutral m-4">
|
||||
<div className="stat">
|
||||
<div className="stat-title">Users</div>
|
||||
<div className="stat-value">
|
||||
{usersCountLoading || usersCountFetching ? (
|
||||
<span className="loading loading-dots loading-lg"></span>
|
||||
) : (
|
||||
<>{usersCount ? usersCount : "0"}</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="stat">
|
||||
<div className="stat-title">Rooms</div>
|
||||
<div className="stat-value">
|
||||
{roomsCountLoading || roomsCountFetching ? (
|
||||
<span className="loading loading-dots loading-lg"></span>
|
||||
) : (
|
||||
<>{roomsCount ? roomsCount : "0"}</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="stat">
|
||||
<div className="stat-title">Votes</div>
|
||||
<div className="stat-value">
|
||||
{votesCountLoading || votesCountFetching ? (
|
||||
<span className="loading loading-dots loading-lg"></span>
|
||||
) : (
|
||||
<>{votesCount ? votesCount : "0"}</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Stats;
|
|
@ -8,6 +8,7 @@ import { SiGithub, SiGoogle } from "react-icons/si";
|
|||
import { GiStarFormation } from "react-icons/gi";
|
||||
import { api } from "~/utils/api";
|
||||
import { getServerAuthSession } from "../../server/auth";
|
||||
import Stats from "~/components/Stats";
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async (ctx) => {
|
||||
const session = await getServerAuthSession(ctx);
|
||||
|
@ -55,31 +56,13 @@ const Admin: NextPage = () => {
|
|||
|
||||
export default Admin;
|
||||
|
||||
const AdminBody: React.FC = () => {
|
||||
const {
|
||||
data: usersCount,
|
||||
isLoading: usersCountLoading,
|
||||
isFetching: usersCountFetching,
|
||||
refetch: refetchUsersCount,
|
||||
} = api.rest.userCount.useQuery();
|
||||
const AdminBody = () => {
|
||||
const {
|
||||
data: users,
|
||||
isLoading: usersLoading,
|
||||
isFetching: usersFetching,
|
||||
refetch: refetchUsers,
|
||||
} = api.user.getAll.useQuery();
|
||||
const {
|
||||
data: roomsCount,
|
||||
isLoading: roomsCountLoading,
|
||||
isFetching: roomsCountFetching,
|
||||
refetch: refetchRoomsCount,
|
||||
} = api.rest.roomCount.useQuery();
|
||||
const {
|
||||
data: votesCount,
|
||||
isLoading: votesCountLoading,
|
||||
isFetching: votesCountFetching,
|
||||
refetch: refetchVotesCount,
|
||||
} = api.rest.voteCount.useQuery();
|
||||
|
||||
const getProviders = (user: {
|
||||
createdAt: Date;
|
||||
|
@ -153,57 +136,16 @@ const AdminBody: React.FC = () => {
|
|||
};
|
||||
|
||||
const refetchData = async () => {
|
||||
await Promise.all([
|
||||
refetchUsers(),
|
||||
refetchUsersCount(),
|
||||
refetchRoomsCount(),
|
||||
refetchVotesCount(),
|
||||
]);
|
||||
await Promise.all([refetchUsers()]);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1 className="text-4xl font-bold">Admin Panel</h1>
|
||||
|
||||
<div className="stats stats-horizontal shadow bg-neutral m-4">
|
||||
<div className="stat">
|
||||
<div className="stat-title">Users</div>
|
||||
<div className="stat-value">
|
||||
{usersCountLoading || usersCountFetching ? (
|
||||
<span className="loading loading-dots loading-lg"></span>
|
||||
) : (
|
||||
<>{usersCount ? usersCount : "0"}</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<Stats />
|
||||
|
||||
<div className="stat">
|
||||
<div className="stat-title">Rooms</div>
|
||||
<div className="stat-value">
|
||||
{roomsCountLoading || roomsCountFetching ? (
|
||||
<span className="loading loading-dots loading-lg"></span>
|
||||
) : (
|
||||
<>{roomsCount ? roomsCount : "0"}</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="stat">
|
||||
<div className="stat-title">Votes</div>
|
||||
<div className="stat-value">
|
||||
{votesCountLoading || votesCountFetching ? (
|
||||
<span className="loading loading-dots loading-lg"></span>
|
||||
) : (
|
||||
<>{votesCount ? votesCount : "0"}</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{usersCountFetching ||
|
||||
usersFetching ||
|
||||
roomsCountFetching ||
|
||||
votesCountFetching ? (
|
||||
{usersFetching ? (
|
||||
<span className="loading loading-dots loading-lg"></span>
|
||||
) : (
|
||||
<div className="flex flex-row flex-wrap text-center items-center justify-center gap-2">
|
||||
|
|
|
@ -45,7 +45,7 @@ const Home: NextPage = () => {
|
|||
|
||||
export default Home;
|
||||
|
||||
const HomePageBody: React.FC = () => {
|
||||
const HomePageBody = () => {
|
||||
const { data: sessionData } = useSession();
|
||||
const [joinRoomTextBox, setJoinRoomTextBox] = useState<string>("");
|
||||
const [tabIndex, setTabIndex] = useState<number>();
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { type NextPage } from "next";
|
||||
import Head from "next/head";
|
||||
import Stats from "~/components/Stats";
|
||||
|
||||
const Home: NextPage = () => {
|
||||
return (
|
||||
|
@ -17,7 +18,7 @@ const Home: NextPage = () => {
|
|||
|
||||
export default Home;
|
||||
|
||||
const HomePageBody: React.FC = () => {
|
||||
const HomePageBody = () => {
|
||||
return (
|
||||
<>
|
||||
<h1 className="text-3xl sm:text-6xl font-bold">
|
||||
|
@ -54,6 +55,13 @@ const HomePageBody: React.FC = () => {
|
|||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="card card-compact bg-secondary text-black font-bold text-left">
|
||||
<div className="card-body">
|
||||
<h2 className="card-title">Stats:</h2>
|
||||
<Stats />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -48,7 +48,7 @@ const Profile: NextPage = () => {
|
|||
|
||||
export default Profile;
|
||||
|
||||
const ProfileBody: React.FC = () => {
|
||||
const ProfileBody = () => {
|
||||
const { data: sessionData } = useSession();
|
||||
const [nameText, setNameText] = useState<string>("");
|
||||
const router = useRouter();
|
||||
|
|
|
@ -65,7 +65,7 @@ const Room: NextPage = () => {
|
|||
|
||||
export default Room;
|
||||
|
||||
const RoomBody: React.FC = ({}) => {
|
||||
const RoomBody = ({}) => {
|
||||
const { data: sessionData } = useSession();
|
||||
const { query } = useRouter();
|
||||
const roomId = z.string().parse(query.id);
|
||||
|
|
|
@ -33,6 +33,12 @@ export const roomRouter = createTRPCRouter({
|
|||
EventTypes.ROOM_LIST_UPDATE,
|
||||
JSON.stringify(room)
|
||||
);
|
||||
|
||||
await publishToChannel(
|
||||
`stats`,
|
||||
EventTypes.STATS_UPDATE,
|
||||
JSON.stringify(room)
|
||||
);
|
||||
}
|
||||
// happy path
|
||||
return !!room;
|
||||
|
@ -224,6 +230,12 @@ export const roomRouter = createTRPCRouter({
|
|||
EventTypes.ROOM_UPDATE,
|
||||
JSON.stringify(deletedRoom)
|
||||
);
|
||||
|
||||
await publishToChannel(
|
||||
`stats`,
|
||||
EventTypes.STATS_UPDATE,
|
||||
JSON.stringify(deletedRoom)
|
||||
);
|
||||
}
|
||||
|
||||
return !!deletedRoom;
|
||||
|
|
|
@ -3,6 +3,7 @@ import { Resend } from "resend";
|
|||
import { z } from "zod";
|
||||
import { Goodbye } from "~/components/templates/Goodbye";
|
||||
import { env } from "~/env.mjs";
|
||||
import { publishToChannel } from "~/server/ably";
|
||||
import {
|
||||
adminProcedure,
|
||||
createTRPCRouter,
|
||||
|
@ -10,6 +11,7 @@ import {
|
|||
} from "~/server/api/trpc";
|
||||
|
||||
import { fetchCache, invalidateCache, setCache } from "~/server/redis";
|
||||
import { EventTypes } from "~/utils/types";
|
||||
|
||||
const resend = new Resend(process.env.RESEND_API_KEY);
|
||||
|
||||
|
@ -118,6 +120,12 @@ export const userRouter = createTRPCRouter({
|
|||
|
||||
await invalidateCache(`kv_usercount_admin`);
|
||||
await invalidateCache(`kv_userlist_admin`);
|
||||
|
||||
await publishToChannel(
|
||||
`stats`,
|
||||
EventTypes.STATS_UPDATE,
|
||||
JSON.stringify(user)
|
||||
);
|
||||
}
|
||||
|
||||
return !!user;
|
||||
|
|
|
@ -93,6 +93,12 @@ export const voteRouter = createTRPCRouter({
|
|||
EventTypes.VOTE_UPDATE,
|
||||
input.value
|
||||
);
|
||||
|
||||
await publishToChannel(
|
||||
`stats`,
|
||||
EventTypes.STATS_UPDATE,
|
||||
JSON.stringify(vote)
|
||||
);
|
||||
}
|
||||
|
||||
return !!vote;
|
||||
|
|
|
@ -12,6 +12,8 @@ import { env } from "~/env.mjs";
|
|||
import { prisma } from "~/server/db";
|
||||
import { Welcome } from "../components/templates/Welcome";
|
||||
import { invalidateCache } from "./redis";
|
||||
import { publishToChannel } from "./ably";
|
||||
import { EventTypes } from "~/utils/types";
|
||||
|
||||
const resend = new Resend(process.env.RESEND_API_KEY);
|
||||
|
||||
|
@ -65,6 +67,12 @@ export const authOptions: NextAuthOptions = {
|
|||
});
|
||||
await invalidateCache(`kv_userlist_admin`);
|
||||
await invalidateCache(`kv_usercount_admin`);
|
||||
|
||||
await publishToChannel(
|
||||
`stats`,
|
||||
EventTypes.STATS_UPDATE,
|
||||
JSON.stringify(user)
|
||||
);
|
||||
}
|
||||
},
|
||||
async signIn({}) {
|
||||
|
|
|
@ -4,6 +4,7 @@ export const EventTypes = {
|
|||
ROOM_LIST_UPDATE: "room.list.update",
|
||||
ROOM_UPDATE: "room.update",
|
||||
VOTE_UPDATE: "vote.update",
|
||||
STATS_UPDATE: "stats.update",
|
||||
} as const;
|
||||
export type EventType = BetterEnum<typeof EventTypes>;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue