Skip to content

Commit 66b5d11

Browse files
committed
home page info
1 parent 9d90379 commit 66b5d11

File tree

4 files changed

+337
-2
lines changed

4 files changed

+337
-2
lines changed

apps/sensenet/src/application-paths.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export const PATHS = {
2020
apiKeys: { appPath: '/system/apikeys' },
2121
landingPath: { appPath: '/content/explorer/' },
2222
root: { appPath: '/Root', snPath: '/Root' },
23+
home: { appPath: '/', snPath: '/' },
2324
} as const
2425

2526
type SettingsItemType = 'stats' | 'settings' | 'apikeys' | 'webhooks' | 'adminui'
Lines changed: 18 additions & 0 deletions
Loading
Lines changed: 315 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
/* eslint-disable import/order */
2+
import {
3+
createStyles,
4+
makeStyles,
5+
Table,
6+
TableBody,
7+
TableCell,
8+
TableHead,
9+
TableRow,
10+
Theme,
11+
Tooltip,
12+
} from '@material-ui/core'
13+
import { useRepository } from '@sensenet/hooks-react'
14+
import React, { lazy, useEffect, useState } from 'react'
15+
import logoUrl from '../assets/sensenet_white.png'
16+
import { DateTimeFormatter } from './grid/Formatters/DateTimeFormatter'
17+
import Accordion from '@material-ui/core/Accordion'
18+
import AccordionSummary from '@material-ui/core/AccordionSummary'
19+
import AccordionDetails from '@material-ui/core/AccordionDetails'
20+
import Typography from '@material-ui/core/Typography'
21+
import ExpandMoreIcon from '@material-ui/icons/ExpandMore'
22+
import { useAuth } from '../context/auth-provider'
23+
24+
const DashboardComponent = lazy(() => import(/* webpackChunkName: "dashboard" */ './dashboard'))
25+
26+
const useStyles = makeStyles((theme: Theme) =>
27+
createStyles({
28+
homeCont: {
29+
display: 'flex',
30+
flexDirection: 'column',
31+
width: '100%',
32+
height: '100%',
33+
justifyContent: 'center',
34+
alignItems: 'center',
35+
color: theme.palette.type === 'light' ? theme.palette.common.black : theme.palette.common.white,
36+
},
37+
title: {
38+
display: 'flex',
39+
gap: '32px',
40+
padding: '16px',
41+
justifyContent: 'center',
42+
alignItems: 'center',
43+
'& img': {
44+
backgroundColor: 'black',
45+
filter: theme.palette.type === 'light' ? 'invert(1)' : '',
46+
},
47+
'& h1': {
48+
fontSize: '32px',
49+
},
50+
},
51+
sensenetLogo: {
52+
width: 60,
53+
height: 60,
54+
},
55+
gridsCont: {
56+
width: '100%',
57+
height: '100%',
58+
overflow: 'auto',
59+
display: 'flex',
60+
flexDirection: 'column',
61+
},
62+
gridCont: {
63+
width: '100%',
64+
height: '100%',
65+
display: 'flex',
66+
flexDirection: 'column',
67+
padding: '0 20px 20px 20px',
68+
border: theme.palette.type === 'light' ? '1px solid #DBDBDB' : '1px solid #2c2c2c',
69+
'& h2': {
70+
textAlign: 'center',
71+
},
72+
overflow: 'auto',
73+
},
74+
accordion: {
75+
backgroundColor: theme.palette.type === 'light' ? '#f9f9f9' : '#333',
76+
color: theme.palette.type === 'light' ? theme.palette.common.black : theme.palette.common.white,
77+
marginBottom: '8px',
78+
},
79+
accordionSummary: {
80+
display: 'flex',
81+
alignItems: 'center',
82+
'& .MuiAccordionSummary-content': {
83+
display: 'grid',
84+
gridTemplateColumns: '1fr 1fr 1fr 3fr',
85+
gap: '16px',
86+
[theme.breakpoints.down(600)]: {
87+
gridTemplateColumns: '1fr',
88+
},
89+
},
90+
},
91+
accordionDetails: {
92+
padding: '16px',
93+
borderTop: `1px solid ${theme.palette.type === 'light' ? '#DBDBDB' : '#2c2c2c'}`,
94+
},
95+
truncatedText: {
96+
overflow: 'hidden',
97+
textOverflow: 'ellipsis',
98+
whiteSpace: 'nowrap',
99+
},
100+
fixedItem: {
101+
maxWidth: '150px',
102+
flexShrink: 0,
103+
overflow: 'hidden',
104+
textOverflow: 'ellipsis',
105+
whiteSpace: 'nowrap',
106+
marginRight: '1rem',
107+
},
108+
109+
contentPathItem: {
110+
flexGrow: 1,
111+
minWidth: 0,
112+
},
113+
}),
114+
)
115+
116+
export const Home = () => {
117+
const classes = useStyles()
118+
const repo = useRepository()
119+
const { user } = useAuth()
120+
121+
const [lastMinuteLogs, setLastMinuteLogs] = useState<any[]>([])
122+
const [myLogs, setMyLogs] = useState<any[]>([])
123+
const [hasGetLogs, setHasGetLogs] = useState(false)
124+
const [hasGetTopLogsByUser, setHasGetTopLogsByUser] = useState(false)
125+
126+
useEffect(() => {
127+
async function getActionsAndLogs() {
128+
try {
129+
const { d } = await repo.getActions({ idOrPath: '/Root' })
130+
const actionNames = d.results.map((a: any) => a.Name)
131+
132+
const canGetLogs = actionNames.includes('GetLogsForLastMinutes')
133+
const canGetUserLogs = actionNames.includes('GetTopLogsByUser')
134+
135+
setHasGetLogs(canGetLogs)
136+
setHasGetTopLogsByUser(canGetUserLogs)
137+
138+
if (canGetLogs) await getLatestChanges()
139+
if (canGetUserLogs) await getMyChanges()
140+
} catch (error: any) {
141+
console.error('Fetching actions failed:', error.message)
142+
}
143+
}
144+
145+
async function getLatestChanges() {
146+
try {
147+
const response = await repo.executeAction<any[], any>({
148+
idOrPath: '/Root',
149+
name: 'LogEntries/GetLogsForLastMinutes',
150+
method: 'GET',
151+
oDataOptions: {
152+
top: 100,
153+
minutes: 600,
154+
} as any,
155+
})
156+
157+
if (response) {
158+
setLastMinuteLogs(response.filter((res: any) => res.ContentPath !== '/Root/System/Cache/DatabaseUsage.cache'))
159+
} else {
160+
setHasGetLogs(false)
161+
}
162+
} catch (error: any) {
163+
console.error('GetLogsForLastMinutes error:', error.message)
164+
setHasGetLogs(false)
165+
}
166+
}
167+
168+
async function getMyChanges() {
169+
try {
170+
const response = await repo.executeAction<any[], any>({
171+
idOrPath: '/Root',
172+
name: 'LogEntries/GetTopLogsByUser',
173+
method: 'GET',
174+
oDataOptions: {
175+
userName: user?.LoginName,
176+
top: 100,
177+
} as any,
178+
})
179+
if (response) {
180+
setMyLogs(response)
181+
} else {
182+
setHasGetTopLogsByUser(false)
183+
}
184+
} catch (error: any) {
185+
console.error('GetTopLogsByUser error:', error.message)
186+
setHasGetTopLogsByUser(false)
187+
}
188+
}
189+
190+
getActionsAndLogs()
191+
// eslint-disable-next-line react-hooks/exhaustive-deps
192+
}, [])
193+
194+
const getTable = (log: any) => {
195+
const changes = log.ExtendedProperties?.ChangedData
196+
if (!changes) return null
197+
const rows = changes
198+
.map((change: any[]) => {
199+
const nameObj = change.find((obj) => obj.name)
200+
const oldValueObj = change.find((obj) => obj.oldValue)
201+
const newValueObj = change.find((obj) => obj.newValue)
202+
203+
if (!nameObj || nameObj.name === 'raw') return null
204+
205+
return {
206+
key: nameObj.name,
207+
oldValue: oldValueObj?.oldValue ?? '—',
208+
newValue: newValueObj?.newValue ?? '—',
209+
}
210+
})
211+
.filter(Boolean)
212+
return (
213+
<Table size="small" style={{ width: '100%' }}>
214+
<TableHead>
215+
<TableRow>
216+
<TableCell />
217+
<TableCell>
218+
<strong>Old Value</strong>
219+
</TableCell>
220+
<TableCell>
221+
<strong>New Value</strong>
222+
</TableCell>
223+
</TableRow>
224+
</TableHead>
225+
<TableBody>
226+
{rows.map((row: any, idx: any) => (
227+
<TableRow key={idx}>
228+
<TableCell style={{ verticalAlign: 'top' }}>
229+
<strong>{row.key}</strong>
230+
</TableCell>
231+
<TableCell>{row.oldValue}</TableCell>
232+
<TableCell>{row.newValue}</TableCell>
233+
</TableRow>
234+
))}
235+
</TableBody>
236+
</Table>
237+
)
238+
}
239+
240+
return !hasGetLogs && !hasGetTopLogsByUser ? (
241+
<DashboardComponent />
242+
) : (
243+
<div className={classes.homeCont}>
244+
<div className={classes.title}>
245+
<img src={logoUrl} alt="Logo" className={classes.sensenetLogo} />
246+
<h1>Sensenet</h1>
247+
</div>
248+
<div className={classes.gridsCont}>
249+
{hasGetLogs && (
250+
<div className={classes.gridCont}>
251+
<h2>Latest Changes</h2>
252+
{lastMinuteLogs.map((log, index) => {
253+
const hasChanges = log.ExtendedProperties?.ChangedData?.length > 0
254+
return (
255+
<Accordion key={index} className={classes.accordion} disabled={!hasChanges}>
256+
<AccordionSummary
257+
expandIcon={hasChanges ? <ExpandMoreIcon /> : null}
258+
aria-controls={`panel${index}-content`}
259+
id={`panel${index}-header`}
260+
className={classes.accordionSummary}>
261+
<Typography className={classes.fixedItem}>
262+
{log.LogDate ? DateTimeFormatter({ value: log.LogDate }) : ''}
263+
</Typography>
264+
<Typography className={classes.fixedItem}>{log.Message || ''}</Typography>
265+
<Typography className={classes.fixedItem}>{log.UserName || ''}</Typography>
266+
<Tooltip title={log.ContentPath || ''} arrow>
267+
<Typography className={`${classes.contentPathItem} ${classes.truncatedText}`}>
268+
{log.ContentPath || ''}
269+
</Typography>
270+
</Tooltip>
271+
</AccordionSummary>
272+
{hasChanges && (
273+
<AccordionDetails className={classes.accordionDetails}>{getTable(log)}</AccordionDetails>
274+
)}
275+
</Accordion>
276+
)
277+
})}
278+
</div>
279+
)}
280+
281+
{hasGetTopLogsByUser && (
282+
<div className={classes.gridCont}>
283+
<h2>My Changes</h2>
284+
{myLogs.map((log, index) => {
285+
const hasChanges = log.ExtendedProperties?.ChangedData?.length > 0
286+
return (
287+
<Accordion key={index} className={classes.accordion} disabled={!hasChanges}>
288+
<AccordionSummary
289+
expandIcon={hasChanges ? <ExpandMoreIcon /> : null}
290+
aria-controls={`panel${index}-content`}
291+
id={`panel${index}-header`}
292+
className={classes.accordionSummary}>
293+
<Typography className={classes.fixedItem}>
294+
{log.LogDate ? DateTimeFormatter({ value: log.LogDate }) : ''}
295+
</Typography>
296+
<Typography className={classes.fixedItem}>{log.Message || ''}</Typography>
297+
<Typography className={classes.fixedItem}>{log.UserName || ''}</Typography>
298+
<Tooltip title={log.ContentPath || ''} arrow>
299+
<Typography className={`${classes.contentPathItem} ${classes.truncatedText}`}>
300+
{log.ContentPath || ''}
301+
</Typography>
302+
</Tooltip>
303+
</AccordionSummary>
304+
{hasChanges && (
305+
<AccordionDetails className={classes.accordionDetails}>{getTable(log)}</AccordionDetails>
306+
)}
307+
</Accordion>
308+
)
309+
})}
310+
</div>
311+
)}
312+
</div>
313+
</div>
314+
)
315+
}

apps/sensenet/src/components/MainRouter.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { ErrorBoundary } from './error-boundary'
88
import { ErrorBoundaryWithDialogs } from './error-boundary-with-dialogs'
99
import { FullScreenLoader } from './full-screen-loader'
1010
import { contentTemplatesColumnDefs } from './grid/Cols/ColumnDefs.'
11+
import { Home } from './Home'
1112

1213
const ContentComponent = lazy(() => import(/* webpackChunkName: "content" */ './content'))
1314
const DashboardComponent = lazy(() => import(/* webpackChunkName: "dashboard" */ './dashboard'))
@@ -84,8 +85,8 @@ export const MainRouter = () => {
8485
<CustomContent rootPath={PATHS.custom.snPath} />
8586
</Route>
8687

87-
<Route path={PATHS.root.snPath} exact>
88-
<DashboardComponent />
88+
<Route path={PATHS.home.snPath}>
89+
<Home />
8990
</Route>
9091
</Switch>
9192
</Suspense>

0 commit comments

Comments
 (0)