From 595cf6bf465a583bb7e4be67f1b5d1d74ff13a2c Mon Sep 17 00:00:00 2001 From: DirkSchlossmacher <62424946+DirkSchlossmacher@users.noreply.github.com> Date: Tue, 14 Nov 2023 09:11:08 +0100 Subject: [PATCH] usagestats v0 --- .../usage-stats/UsageStats.module.scss | 76 ++++++++++++++++++ app/components/usage-stats/UsageStats.tsx | 79 +++++++++++++++++++ app/utils/cloud/redisRestClient.ts | 32 ++++++++ 3 files changed, 187 insertions(+) create mode 100644 app/components/usage-stats/UsageStats.module.scss create mode 100644 app/components/usage-stats/UsageStats.tsx diff --git a/app/components/usage-stats/UsageStats.module.scss b/app/components/usage-stats/UsageStats.module.scss new file mode 100644 index 000000000..eafe8eb45 --- /dev/null +++ b/app/components/usage-stats/UsageStats.module.scss @@ -0,0 +1,76 @@ +// UsageStats.module.scss + +.usageStatsContainer { + position: fixed; // Fixed position to overlay on top of the content + top: 0; + left: 0; + right: 0; + bottom: 0; + display: flex; + justify-content: center; + align-items: center; + background-color: rgba(0, 0, 0, 0.5); // Semi-transparent background to obscure the content + z-index: 1000; // High z-index to ensure it's on top of other elements + } + + .usageStatsModal { + background: white; + padding: 20px; + border-radius: 8px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + width: 80%; + max-width: 600px; // Maximum width of the modal + z-index: 1001; // Ensure the modal is above the semi-transparent background + } + + // ... Add more styles for headings, buttons, tables, etc. + .closeButton { + position: absolute; + top: 10px; + right: 10px; + border: none; + background: transparent; + cursor: pointer; + + &:hover { + opacity: 0.8; + } + } + .dropdown { + margin-bottom: 10px; + } + + .button { + padding: 10px 20px; + margin-right: 10px; + border: none; + border-radius: 4px; + cursor: pointer; + + &:hover { + background-color: #f0f0f0; + } + } + + // ... Add more styles as needed + .table { + width: 100%; + border-collapse: collapse; + + th, td { + text-align: left; + padding: 8px; + border-bottom: 1px solid #ddd; + } + + th { + background-color: #f8f8f8; + } + } + @media (max-width: 768px) { + .usageStatsModal { + width: 95%; + padding: 10px; + } + } + \ No newline at end of file diff --git a/app/components/usage-stats/UsageStats.tsx b/app/components/usage-stats/UsageStats.tsx new file mode 100644 index 000000000..960db156c --- /dev/null +++ b/app/components/usage-stats/UsageStats.tsx @@ -0,0 +1,79 @@ +// UsageStats.tsx + +import React, { useState, useEffect } from 'react'; +import { getAvailableDateKeys, getSignInCountForPeriod, getDetailsByUser } from './app/utils/cloud/redisRestClient'; +import styles from './UsageStats.module.scss'; // Assume you have a separate SCSS module for UsageStats + +const UsageStats: React.FC<{ onClose: () => void }> = ({ onClose }) => { + const [dateKeys, setDateKeys] = useState([]); + const [selectedDateKey, setSelectedDateKey] = useState(''); + const [signInCount, setSignInCount] = useState(0); + const [userDetails, setUserDetails] = useState>({}); + const [showDrillDown, setShowDrillDown] = useState(false); + + useEffect(() => { + const fetchData = async () => { + const availableDateKeys = await getAvailableDateKeys(); + setDateKeys(availableDateKeys); + }; + fetchData(); + }, []); + + const handleDateChange = async (event: React.ChangeEvent) => { + const dateKey = event.target.value; + setSelectedDateKey(dateKey); + const count = await getSignInCountForPeriod(dateKey); + setSignInCount(count); + setShowDrillDown(false); + }; + + const handleDrillDown = async () => { + const details = await getDetailsByUser(selectedDateKey); + setUserDetails(details); + setShowDrillDown(true); + }; + + return ( +
+
+

Usage Stats

+ +

Number of events: {signInCount}

+ + {showDrillDown && ( + + + {/* ... other UI elements ... */} + + + + + + + + + + {Object.entries(userDetails).map(([email, count]) => ( + + + + + ))} + +
EmailCount
{email}{count}
+ )} + +
+
+ ); +}; + +export default UsageStats; \ No newline at end of file diff --git a/app/utils/cloud/redisRestClient.ts b/app/utils/cloud/redisRestClient.ts index 726dfb730..90031e75d 100644 --- a/app/utils/cloud/redisRestClient.ts +++ b/app/utils/cloud/redisRestClient.ts @@ -60,3 +60,35 @@ export const incrementTokenCounts = async ( console.error('Failed to increment token counts in Redis via Upstash', error); } }; + + +export const getAvailableDateKeys = async (): Promise => { + try { + const keys = await redis.keys('signin_count:*'); + return keys.map(key => key.split(':')[1]); + } catch (error) { + console.error('Failed to get keys from Redis', error); + return []; + } +}; + +export const getSignInCountForPeriod = async (dateKey: string): Promise => { + try { + const counts = await redis.hgetall(`signin_count:${dateKey}`); + return Object.values(counts).reduce((total, count) => total + parseInt(count, 10), 0); + } catch (error) { + console.error(`Failed to get sign-in count for period ${dateKey}`, error); + return 0; + } +}; + +export const getDetailsByUser = async (dateKey: string): Promise> => { + try { + const counts = await redis.hgetall(`signin_count:${dateKey}`); + return counts; + } catch (error) { + console.error(`Failed to get details by user for period ${dateKey}`, error); + return {}; + } +}; +