File tree Expand file tree Collapse file tree 9 files changed +163
-7
lines changed
Expand file tree Collapse file tree 9 files changed +163
-7
lines changed Original file line number Diff line number Diff line change @@ -33,3 +33,12 @@ yarn-error.log*
3333
3434# typescript
3535* .tsbuildinfo
36+
37+ # data
38+ /data
39+
40+ # iml
41+ * .iml
42+
43+ # xml
44+ * .xml
Original file line number Diff line number Diff line change @@ -12,6 +12,7 @@ RUN npm install
1212COPY components ./components
1313COPY pages ./pages
1414COPY public ./public
15+ COPY services ./services
1516COPY styles ./styles
1617COPY utils ./utils
1718COPY next-env.d.ts ./
Original file line number Diff line number Diff line change @@ -33,18 +33,38 @@ const VersionNumber: React.FC<{ version: string }> = ({version}) => (
3333) ;
3434
3535interface BottomBarProps {
36- version : string
36+ version : string ;
37+ visits ?: number ;
38+ startDate ?: string ;
3739}
3840
39- const BottomBar : React . FC < BottomBarProps > = ( { version} ) => {
41+ const BottomBar : React . FC < BottomBarProps > = ( { version, visits , startDate } ) => {
4042 const colorMode = useContext ( ColorModeContext ) ;
4143 const theme = useTheme ( ) ;
4244
45+ const formattedDate = startDate ? new Date ( startDate ) . toLocaleDateString ( 'en-US' , {
46+ year : 'numeric' ,
47+ month : 'long' ,
48+ day : 'numeric'
49+ } ) : null ;
50+
4351 return (
4452 < AppBar position = "static" sx = { ( theme ) => bottomAppBarStyle ( theme ) } >
4553 < Toolbar sx = { ( theme ) => toolbarStyle ( theme ) } >
4654 < Box sx = { ( theme ) => flexContainerStyle ( theme ) } >
47- < VersionNumber version = { version } />
55+ < Box sx = { { display : 'flex' , alignItems : 'center' , gap : 2 } } >
56+ < VersionNumber version = { version } />
57+ { visits !== undefined && startDate && (
58+ < >
59+ < Typography variant = "body2" color = "inherit" >
60+ •
61+ </ Typography >
62+ < Typography variant = "body2" color = "inherit" >
63+ { visits } visits since { formattedDate }
64+ </ Typography >
65+ </ >
66+ ) }
67+ </ Box >
4868
4969 < Box sx = { ( theme ) => flexContainerStyle ( theme , { gap : 1 } ) } >
5070 < FooterButton
Original file line number Diff line number Diff line change 1+ import React from 'react' ;
2+ import { Typography , Box } from '@mui/material' ;
3+
4+ interface VisitCounterProps {
5+ visits : number ;
6+ startDate : string ;
7+ }
8+
9+ const VisitCounter : React . FC < VisitCounterProps > = ( { visits, startDate } ) => {
10+ const formattedDate = new Date ( startDate ) . toLocaleDateString ( 'en-US' , {
11+ year : 'numeric' ,
12+ month : 'long' ,
13+ day : 'numeric'
14+ } ) ;
15+
16+ return (
17+ < Box
18+ sx = { {
19+ position : 'fixed' ,
20+ bottom : '80px' ,
21+ right : '20px' ,
22+ padding : '8px' ,
23+ borderRadius : '4px' ,
24+ backgroundColor : 'background.paper' ,
25+ boxShadow : 1
26+ } }
27+ >
28+ < Typography variant = "body2" >
29+ Page Visits: { visits }
30+ </ Typography >
31+ < Typography variant = "body2" sx = { { fontSize : '0.8em' , opacity : 0.8 } } >
32+ Since { formattedDate }
33+ </ Typography >
34+ </ Box >
35+ ) ;
36+ } ;
37+
38+ export default VisitCounter ;
Original file line number Diff line number Diff line change @@ -6,5 +6,5 @@ services:
66 - " 3000:3000"
77 volumes :
88 - ./node_modules:/app/node_modules
9- restart : always
10-
9+ - ./data:/app/data
10+ restart : always
Original file line number Diff line number Diff line change 1+ import { NextApiRequest , NextApiResponse } from 'next' ;
2+ import { initializeVisitStorage , getVisitData , incrementVisitCount } from '../../utils/visitStorage' ;
3+
4+ initializeVisitStorage ( ) ;
5+
6+ export default function handler ( req : NextApiRequest , res : NextApiResponse ) {
7+ if ( req . method === 'POST' ) {
8+ const data = incrementVisitCount ( ) ;
9+ res . status ( 200 ) . json ( data ) ;
10+ } else if ( req . method === 'GET' ) {
11+ const data = getVisitData ( ) ;
12+ res . status ( 200 ) . json ( data ) ;
13+ } else {
14+ res . status ( 405 ) . json ( { message : 'Method not allowed' } ) ;
15+ }
16+ }
Original file line number Diff line number Diff line change @@ -5,6 +5,7 @@ import Blurb from '../components/Blurb'
55import PluginCard from '../components/PluginCard'
66import React from 'react' ;
77import BottomBar from '../components/BottomBar'
8+ import { getVisits , incrementVisits } from '../services/visitService' ;
89
910interface PluginData {
1011 mostPopular : string [ ] ;
@@ -80,7 +81,24 @@ const AllPlugins: React.FC = () => (
8081 </ Box >
8182)
8283
83- const Home : NextPage = ( ) => {
84+ interface HomeProps {
85+ visits : number ;
86+ startDate : string ;
87+ }
88+
89+ export const getServerSideProps = async ( ) => {
90+ await incrementVisits ( ) ;
91+ const data = await getVisits ( ) ;
92+
93+ return {
94+ props : {
95+ visits : data . visits ,
96+ startDate : data . startDate
97+ }
98+ } ;
99+ } ;
100+
101+ const Home : NextPage < HomeProps > = ( { visits, startDate } ) => {
84102 return (
85103 < Box sx = { pageStyle } >
86104 < TopBar />
@@ -91,7 +109,11 @@ const Home: NextPage = () => {
91109 < SectionDivider />
92110 < AllPlugins />
93111 </ Container >
94- < BottomBar version = { version } />
112+ < BottomBar
113+ version = { version }
114+ visits = { visits }
115+ startDate = { startDate }
116+ />
95117 </ Box >
96118 ) ;
97119} ;
Original file line number Diff line number Diff line change 1+ interface VisitData {
2+ visits : number ;
3+ startDate : string ;
4+ }
5+
6+ const getBaseUrl = ( ) => process . env . NEXT_PUBLIC_BASE_URL || 'http://localhost:3000' ;
7+
8+ export const incrementVisits = async ( ) : Promise < void > => {
9+ await fetch ( `${ getBaseUrl ( ) } /api/visits` , { method : 'POST' } ) ;
10+ } ;
11+
12+ export const getVisits = async ( ) : Promise < VisitData > => {
13+ const response = await fetch ( `${ getBaseUrl ( ) } /api/visits` ) ;
14+ return response . json ( ) ;
15+ } ;
Original file line number Diff line number Diff line change 1+ import fs from 'fs' ;
2+ import path from 'path' ;
3+
4+ const DATA_DIR = path . join ( process . cwd ( ) , 'data' ) ;
5+ const VISITS_FILE = path . join ( DATA_DIR , 'visits.json' ) ;
6+
7+ interface VisitData {
8+ visits : number ;
9+ startDate : string ;
10+ }
11+
12+ export const initializeVisitStorage = ( ) => {
13+ if ( ! fs . existsSync ( DATA_DIR ) ) {
14+ fs . mkdirSync ( DATA_DIR , { recursive : true } ) ;
15+ }
16+
17+ if ( ! fs . existsSync ( VISITS_FILE ) ) {
18+ const initialData : VisitData = {
19+ visits : 0 ,
20+ startDate : new Date ( ) . toISOString ( )
21+ } ;
22+ fs . writeFileSync ( VISITS_FILE , JSON . stringify ( initialData ) ) ;
23+ }
24+ } ;
25+
26+ export const getVisitData = ( ) : VisitData => {
27+ return JSON . parse ( fs . readFileSync ( VISITS_FILE , 'utf8' ) ) ;
28+ } ;
29+
30+ export const incrementVisitCount = ( ) : VisitData => {
31+ const data = getVisitData ( ) ;
32+ data . visits += 1 ;
33+ fs . writeFileSync ( VISITS_FILE , JSON . stringify ( data ) ) ;
34+ return data ;
35+ } ;
You can’t perform that action at this time.
0 commit comments