@hedhog/admin 0.48.16 → 0.48.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/auth.service.d.ts.map +1 -1
- package/dist/auth/auth.service.js +1 -1
- package/dist/auth/auth.service.js.map +1 -1
- package/dist/menu/menu.controller.d.ts +3 -3
- package/dist/menu/menu.service.d.ts +3 -3
- package/frontend/dashboard/components/create-panel.tsx.ejs +63 -63
- package/frontend/dashboard/components/dashboard.screen.tsx.ejs +116 -116
- package/frontend/dashboard/components/database-stats.tsx.ejs +126 -0
- package/frontend/dashboard/components/module-installed.tsx.ejs +113 -0
- package/frontend/dashboard/components/system-info.tsx.ejs +144 -0
- package/frontend/dashboard/components/update-panel.tsx.ejs +80 -80
- package/frontend/dashboard/components/user-summary.tsx.ejs +151 -0
- package/frontend/dashboard/locales/en/dashboard.dashboard.json +11 -11
- package/frontend/dashboard/locales/pt/dashboard.dashboard.json +11 -11
- package/frontend/dashboard/react-query/handlers.ts.ejs +28 -28
- package/frontend/dashboard/react-query/requests.ts.ejs +56 -56
- package/frontend/dashboard-component/components/create-panel.tsx.ejs +130 -130
- package/frontend/dashboard-component/components/update-panel.tsx.ejs +164 -164
- package/frontend/dashboard-component/locales/en/dashboard.dashboard-component.json +11 -11
- package/frontend/dashboard-component/locales/pt/dashboard.dashboard-component.json +11 -11
- package/frontend/dashboard-component/react-query/handlers.ts.ejs +28 -28
- package/frontend/dashboard-component/react-query/requests.ts.ejs +61 -61
- package/frontend/dashboard-item/components/create-panel.tsx.ejs +108 -108
- package/frontend/dashboard-item/components/update-panel.tsx.ejs +141 -141
- package/frontend/dashboard-item/locales/en/dashboard.dashboard-item.json +11 -11
- package/frontend/dashboard-item/locales/pt/dashboard.dashboard-item.json +11 -11
- package/frontend/dashboard-item/react-query/handlers.ts.ejs +28 -28
- package/frontend/dashboard-item/react-query/requests.ts.ejs +58 -58
- package/frontend/dashboard-user/components/create-panel.tsx.ejs +108 -108
- package/frontend/dashboard-user/components/update-panel.tsx.ejs +137 -137
- package/frontend/dashboard-user/locales/en/dashboard.dashboard-user.json +11 -11
- package/frontend/dashboard-user/locales/pt/dashboard.dashboard-user.json +11 -11
- package/frontend/dashboard-user/react-query/handlers.ts.ejs +28 -28
- package/frontend/dashboard-user/react-query/requests.ts.ejs +58 -58
- package/hedhog.yaml +17 -0
- package/package.json +1 -1
- package/src/auth/auth.service.ts +1 -3
- package/src/dashboard/dashboard-component/dashboard-component.controller.ts +55 -55
- package/src/dashboard/dashboard-component/dto/create.dto.ts +36 -36
- package/src/dashboard/dashboard-component/dto/update.dto.ts +4 -4
- package/src/dashboard/dashboard-item/dto/update.dto.ts +4 -4
- package/src/dashboard/dashboard-user/dto/update.dto.ts +4 -4
- package/src/dashboard/index.ts +1 -1
@@ -0,0 +1,113 @@
|
|
1
|
+
import { Package } from 'lucide-react'
|
2
|
+
import { useEffect, useState } from 'react'
|
3
|
+
|
4
|
+
import { Badge } from '@/components/ui/badge'
|
5
|
+
import {
|
6
|
+
Card,
|
7
|
+
CardContent,
|
8
|
+
CardDescription,
|
9
|
+
CardHeader,
|
10
|
+
CardTitle,
|
11
|
+
} from '@/components/ui/card'
|
12
|
+
import { useApp } from '@/hooks/use-app'
|
13
|
+
import { DashboardDefaultData } from '@/types'
|
14
|
+
|
15
|
+
const ModuleInstalled = () => {
|
16
|
+
const { request } = useApp()
|
17
|
+
const [isRefreshing, setIsRefreshing] = useState(false)
|
18
|
+
const [data, setData] = useState<DashboardDefaultData>({
|
19
|
+
os: {
|
20
|
+
name: '',
|
21
|
+
platform: '',
|
22
|
+
version: '',
|
23
|
+
architecture: '',
|
24
|
+
uptime: 0,
|
25
|
+
cpu: {
|
26
|
+
model: '',
|
27
|
+
speed: 0,
|
28
|
+
physicalCores: 0,
|
29
|
+
virtualCores: 0,
|
30
|
+
},
|
31
|
+
memory: {
|
32
|
+
total: 0,
|
33
|
+
free: 0,
|
34
|
+
},
|
35
|
+
disk: [],
|
36
|
+
},
|
37
|
+
modules: [],
|
38
|
+
users: {
|
39
|
+
total: 0,
|
40
|
+
admin: 0,
|
41
|
+
active: 0,
|
42
|
+
activities: [],
|
43
|
+
},
|
44
|
+
database: {
|
45
|
+
connections: 0,
|
46
|
+
size: 0,
|
47
|
+
queriesPerSecond: 0,
|
48
|
+
},
|
49
|
+
} as DashboardDefaultData)
|
50
|
+
|
51
|
+
const load = async () => {
|
52
|
+
setIsRefreshing(true)
|
53
|
+
const { data } = await request<DashboardDefaultData>({
|
54
|
+
url: '/core',
|
55
|
+
})
|
56
|
+
|
57
|
+
setData(data)
|
58
|
+
|
59
|
+
setIsRefreshing(false)
|
60
|
+
}
|
61
|
+
|
62
|
+
useEffect(() => {
|
63
|
+
load()
|
64
|
+
}, [])
|
65
|
+
|
66
|
+
return (
|
67
|
+
<Card>
|
68
|
+
<CardHeader className='pb-2'>
|
69
|
+
<CardTitle className='flex items-center'>
|
70
|
+
<Package className='mr-2 h-5 w-5 text-primary' />
|
71
|
+
Módulos Instalados
|
72
|
+
</CardTitle>
|
73
|
+
<CardDescription>Módulos e suas versões</CardDescription>
|
74
|
+
</CardHeader>
|
75
|
+
<CardContent>
|
76
|
+
{isRefreshing ? (
|
77
|
+
<div className='space-y-4'>
|
78
|
+
{Array.from({ length: 5 }).map((_, index) => (
|
79
|
+
<div
|
80
|
+
key={index}
|
81
|
+
className='flex animate-pulse items-center justify-between'
|
82
|
+
>
|
83
|
+
<div>
|
84
|
+
<div className='h-4 w-24 rounded bg-muted'></div>
|
85
|
+
<div className='mt-1 h-3 w-16 rounded bg-muted'></div>
|
86
|
+
</div>
|
87
|
+
<div className='h-6 w-20 rounded bg-muted'></div>
|
88
|
+
</div>
|
89
|
+
))}
|
90
|
+
</div>
|
91
|
+
) : (
|
92
|
+
<div className='space-y-4'>
|
93
|
+
{data.modules.map((module, index) => (
|
94
|
+
<div key={index} className='flex items-center justify-between'>
|
95
|
+
<div>
|
96
|
+
<div className='font-medium'>{module.name}</div>
|
97
|
+
<div className='text-sm text-muted-foreground'>
|
98
|
+
v{String(module.version).replace(/\^/g, '')}
|
99
|
+
</div>
|
100
|
+
</div>
|
101
|
+
<Badge variant={module.upToDate ? 'outline' : 'secondary'}>
|
102
|
+
{module.upToDate ? 'Atualizado' : 'Atualização disponível'}
|
103
|
+
</Badge>
|
104
|
+
</div>
|
105
|
+
))}
|
106
|
+
</div>
|
107
|
+
)}
|
108
|
+
</CardContent>
|
109
|
+
</Card>
|
110
|
+
)
|
111
|
+
}
|
112
|
+
|
113
|
+
export default ModuleInstalled
|
@@ -0,0 +1,144 @@
|
|
1
|
+
import { Cpu, HardDrive, Server } from 'lucide-react'
|
2
|
+
import { useEffect, useState } from 'react'
|
3
|
+
|
4
|
+
import {
|
5
|
+
Card,
|
6
|
+
CardContent,
|
7
|
+
CardDescription,
|
8
|
+
CardHeader,
|
9
|
+
CardTitle,
|
10
|
+
} from '@/components/ui/card'
|
11
|
+
import { Separator } from '@/components/ui/separator'
|
12
|
+
import { useApp } from '@/hooks/use-app'
|
13
|
+
import { bytesToHuman } from '@/lib/bytes-to-human'
|
14
|
+
import { secondsToHuman } from '@/lib/seconds-to-human'
|
15
|
+
import { DashboardDefaultData } from '@/types'
|
16
|
+
|
17
|
+
const SystemInfo = () => {
|
18
|
+
const { request } = useApp()
|
19
|
+
const [isRefreshing, setIsRefreshing] = useState(false)
|
20
|
+
const [data, setData] = useState<DashboardDefaultData>({
|
21
|
+
os: {
|
22
|
+
name: '',
|
23
|
+
platform: '',
|
24
|
+
version: '',
|
25
|
+
architecture: '',
|
26
|
+
uptime: 0,
|
27
|
+
cpu: {
|
28
|
+
model: '',
|
29
|
+
speed: 0,
|
30
|
+
physicalCores: 0,
|
31
|
+
virtualCores: 0,
|
32
|
+
},
|
33
|
+
memory: {
|
34
|
+
total: 0,
|
35
|
+
free: 0,
|
36
|
+
},
|
37
|
+
disk: [],
|
38
|
+
},
|
39
|
+
modules: [],
|
40
|
+
users: {
|
41
|
+
total: 0,
|
42
|
+
admin: 0,
|
43
|
+
active: 0,
|
44
|
+
activities: [],
|
45
|
+
},
|
46
|
+
database: {
|
47
|
+
connections: 0,
|
48
|
+
size: 0,
|
49
|
+
queriesPerSecond: 0,
|
50
|
+
},
|
51
|
+
} as DashboardDefaultData)
|
52
|
+
|
53
|
+
const load = async () => {
|
54
|
+
setIsRefreshing(true)
|
55
|
+
const { data } = await request<DashboardDefaultData>({
|
56
|
+
url: '/core',
|
57
|
+
})
|
58
|
+
|
59
|
+
setData(data)
|
60
|
+
|
61
|
+
setIsRefreshing(false)
|
62
|
+
}
|
63
|
+
|
64
|
+
useEffect(() => {
|
65
|
+
load()
|
66
|
+
}, [])
|
67
|
+
|
68
|
+
return (
|
69
|
+
<Card>
|
70
|
+
<CardHeader className='pb-2'>
|
71
|
+
<CardTitle className='flex items-center'>
|
72
|
+
<Server className='mr-2 h-5 w-5 text-primary' />
|
73
|
+
Informações do Sistema
|
74
|
+
</CardTitle>
|
75
|
+
<CardDescription>
|
76
|
+
Detalhes do sistema operacional e hardware
|
77
|
+
</CardDescription>
|
78
|
+
</CardHeader>
|
79
|
+
<CardContent>
|
80
|
+
{isRefreshing ? (
|
81
|
+
<div className='space-y-4'>
|
82
|
+
<div className='animate-pulse'>
|
83
|
+
<div className='mb-2 h-4 w-3/4 rounded bg-gray-200'></div>
|
84
|
+
<div className='mb-2 h-4 w-1/2 rounded bg-gray-200'></div>
|
85
|
+
<div className='mb-2 h-4 w-1/4 rounded bg-gray-200'></div>
|
86
|
+
<div className='mb-2 h-4 w-2/3 rounded bg-gray-200'></div>
|
87
|
+
</div>
|
88
|
+
<Separator />
|
89
|
+
<div className='animate-pulse'>
|
90
|
+
<div className='mb-2 h-4 w-3/4 rounded bg-gray-200'></div>
|
91
|
+
<div className='mb-2 h-4 w-1/2 rounded bg-gray-200'></div>
|
92
|
+
<div className='mb-2 h-4 w-1/4 rounded bg-gray-200'></div>
|
93
|
+
<div className='mb-2 h-4 w-2/3 rounded bg-gray-200'></div>
|
94
|
+
</div>
|
95
|
+
</div>
|
96
|
+
) : (
|
97
|
+
<div className='space-y-4'>
|
98
|
+
<div>
|
99
|
+
<h3 className='flex items-center text-sm font-medium'>
|
100
|
+
<HardDrive className='mr-2 h-4 w-4 text-muted-foreground' />
|
101
|
+
Sistema Operacional
|
102
|
+
</h3>
|
103
|
+
<div className='mt-1 grid grid-cols-2 gap-1 text-sm'>
|
104
|
+
<div className='text-muted-foreground'>Nome:</div>
|
105
|
+
<div>{data.os.name}</div>
|
106
|
+
<div className='text-muted-foreground'>Versão:</div>
|
107
|
+
<div>{data.os.version}</div>
|
108
|
+
<div className='text-muted-foreground'>Arquitetura:</div>
|
109
|
+
<div>{data.os.architecture}</div>
|
110
|
+
<div className='text-muted-foreground'>Tempo ativo:</div>
|
111
|
+
<div>{secondsToHuman(data.os.uptime, true)}</div>
|
112
|
+
</div>
|
113
|
+
</div>
|
114
|
+
<Separator />
|
115
|
+
<div>
|
116
|
+
<h3 className='flex items-center text-sm font-medium'>
|
117
|
+
<Cpu className='mr-2 h-4 w-4 text-muted-foreground' />
|
118
|
+
Hardware
|
119
|
+
</h3>
|
120
|
+
<div className='mt-1 grid grid-cols-2 gap-1 text-sm'>
|
121
|
+
<div className='text-muted-foreground'>CPU:</div>
|
122
|
+
<div>{data.os.cpu.model}</div>
|
123
|
+
<div className='text-muted-foreground'>Memória:</div>
|
124
|
+
<div>{bytesToHuman(data.os.memory.total)}</div>
|
125
|
+
{data.os.disk.map((disk) => (
|
126
|
+
<>
|
127
|
+
<div className='text-muted-foreground'>
|
128
|
+
Disco {disk.filesystem}:
|
129
|
+
</div>
|
130
|
+
<div>
|
131
|
+
{bytesToHuman(disk.size)} ({bytesToHuman(disk.free)} free)
|
132
|
+
</div>
|
133
|
+
</>
|
134
|
+
))}
|
135
|
+
</div>
|
136
|
+
</div>
|
137
|
+
</div>
|
138
|
+
)}
|
139
|
+
</CardContent>
|
140
|
+
</Card>
|
141
|
+
)
|
142
|
+
}
|
143
|
+
|
144
|
+
export default SystemInfo
|
@@ -1,80 +1,80 @@
|
|
1
|
-
import FormPanel, {
|
2
|
-
FormPanelRef,
|
3
|
-
getFieldsLocale,
|
4
|
-
} from '@/components/panels/form-panel'
|
5
|
-
import { Overlay } from '@/components/custom/overlay'
|
6
|
-
import { TabPanel } from '@/components/panels/tab-panel'
|
7
|
-
import {
|
8
|
-
useDashboardGet,
|
9
|
-
useDashboardUpdate,
|
10
|
-
} from '@/features/admin/dashboard'
|
11
|
-
import useEffectAfterFirstUpdate from '@/hooks/use-effect-after-first-update'
|
12
|
-
import { Dashboard } from '@/types/models'
|
13
|
-
import { forwardRef, useImperativeHandle, useRef } from 'react'
|
14
|
-
import { useTranslation } from 'react-i18next'
|
15
|
-
import { EnumFieldType } from '@/enums/EnumFieldType'
|
16
|
-
|
17
|
-
export type DashboardUpdatePanelProps = {
|
18
|
-
data: Dashboard
|
19
|
-
onUpdated?: (data: Dashboard) => void
|
20
|
-
}
|
21
|
-
|
22
|
-
const DashboardUpdatePanel = forwardRef(
|
23
|
-
({ data, onUpdated }: DashboardUpdatePanelProps, ref) => {
|
24
|
-
const { t } = useTranslation(['actions', 'fields', 'translations'])
|
25
|
-
const { data: item, isLoading } = useDashboardGet(
|
26
|
-
(data as any).dashboard_id as number
|
27
|
-
)
|
28
|
-
const { mutate: dashboardUpdate } = useDashboardUpdate()
|
29
|
-
const formRef = useRef<FormPanelRef>(null)
|
30
|
-
|
31
|
-
useEffectAfterFirstUpdate(() => {
|
32
|
-
if (item && formRef.current) {
|
33
|
-
formRef.current.setValuesFromItem(item)
|
34
|
-
}
|
35
|
-
}, [item])
|
36
|
-
|
37
|
-
useImperativeHandle(ref, () => ({}))
|
38
|
-
|
39
|
-
return (
|
40
|
-
<TabPanel
|
41
|
-
activeTabIndex={0}
|
42
|
-
tabs={[
|
43
|
-
{
|
44
|
-
title: t('details', { ns: 'actions' }),
|
45
|
-
children: (
|
46
|
-
<Overlay loading={isLoading}>
|
47
|
-
<FormPanel
|
48
|
-
ref={formRef}
|
49
|
-
fields={[
|
50
|
-
{
|
51
|
-
name: 'slug',
|
52
|
-
label: { text: t('dashboard.slug', { ns: 'fields' }) },
|
53
|
-
type: EnumFieldType.TEXT,
|
54
|
-
required: true,
|
55
|
-
},
|
56
|
-
...getFieldsLocale([{ name: 'name' }], item),
|
57
|
-
]}
|
58
|
-
button={{ text: t('save', { ns: 'actions' }) }}
|
59
|
-
onSubmit={(data) => {
|
60
|
-
dashboardUpdate({
|
61
|
-
id: data.id,
|
62
|
-
data,
|
63
|
-
})
|
64
|
-
if (typeof onUpdated === 'function') {
|
65
|
-
onUpdated(data)
|
66
|
-
}
|
67
|
-
}}
|
68
|
-
/>
|
69
|
-
</Overlay>
|
70
|
-
),
|
71
|
-
},
|
72
|
-
]}
|
73
|
-
/>
|
74
|
-
)
|
75
|
-
}
|
76
|
-
)
|
77
|
-
|
78
|
-
DashboardUpdatePanel.displayName = 'DashboardUpdatePanel'
|
79
|
-
|
80
|
-
export default DashboardUpdatePanel
|
1
|
+
import FormPanel, {
|
2
|
+
FormPanelRef,
|
3
|
+
getFieldsLocale,
|
4
|
+
} from '@/components/panels/form-panel'
|
5
|
+
import { Overlay } from '@/components/custom/overlay'
|
6
|
+
import { TabPanel } from '@/components/panels/tab-panel'
|
7
|
+
import {
|
8
|
+
useDashboardGet,
|
9
|
+
useDashboardUpdate,
|
10
|
+
} from '@/features/admin/dashboard'
|
11
|
+
import useEffectAfterFirstUpdate from '@/hooks/use-effect-after-first-update'
|
12
|
+
import { Dashboard } from '@/types/models'
|
13
|
+
import { forwardRef, useImperativeHandle, useRef } from 'react'
|
14
|
+
import { useTranslation } from 'react-i18next'
|
15
|
+
import { EnumFieldType } from '@/enums/EnumFieldType'
|
16
|
+
|
17
|
+
export type DashboardUpdatePanelProps = {
|
18
|
+
data: Dashboard
|
19
|
+
onUpdated?: (data: Dashboard) => void
|
20
|
+
}
|
21
|
+
|
22
|
+
const DashboardUpdatePanel = forwardRef(
|
23
|
+
({ data, onUpdated }: DashboardUpdatePanelProps, ref) => {
|
24
|
+
const { t } = useTranslation(['actions', 'fields', 'translations'])
|
25
|
+
const { data: item, isLoading } = useDashboardGet(
|
26
|
+
(data as any).dashboard_id as number
|
27
|
+
)
|
28
|
+
const { mutate: dashboardUpdate } = useDashboardUpdate()
|
29
|
+
const formRef = useRef<FormPanelRef>(null)
|
30
|
+
|
31
|
+
useEffectAfterFirstUpdate(() => {
|
32
|
+
if (item && formRef.current) {
|
33
|
+
formRef.current.setValuesFromItem(item)
|
34
|
+
}
|
35
|
+
}, [item])
|
36
|
+
|
37
|
+
useImperativeHandle(ref, () => ({}))
|
38
|
+
|
39
|
+
return (
|
40
|
+
<TabPanel
|
41
|
+
activeTabIndex={0}
|
42
|
+
tabs={[
|
43
|
+
{
|
44
|
+
title: t('details', { ns: 'actions' }),
|
45
|
+
children: (
|
46
|
+
<Overlay loading={isLoading}>
|
47
|
+
<FormPanel
|
48
|
+
ref={formRef}
|
49
|
+
fields={[
|
50
|
+
{
|
51
|
+
name: 'slug',
|
52
|
+
label: { text: t('dashboard.slug', { ns: 'fields' }) },
|
53
|
+
type: EnumFieldType.TEXT,
|
54
|
+
required: true,
|
55
|
+
},
|
56
|
+
...getFieldsLocale([{ name: 'name' }], item),
|
57
|
+
]}
|
58
|
+
button={{ text: t('save', { ns: 'actions' }) }}
|
59
|
+
onSubmit={(data) => {
|
60
|
+
dashboardUpdate({
|
61
|
+
id: data.id,
|
62
|
+
data,
|
63
|
+
})
|
64
|
+
if (typeof onUpdated === 'function') {
|
65
|
+
onUpdated(data)
|
66
|
+
}
|
67
|
+
}}
|
68
|
+
/>
|
69
|
+
</Overlay>
|
70
|
+
),
|
71
|
+
},
|
72
|
+
]}
|
73
|
+
/>
|
74
|
+
)
|
75
|
+
}
|
76
|
+
)
|
77
|
+
|
78
|
+
DashboardUpdatePanel.displayName = 'DashboardUpdatePanel'
|
79
|
+
|
80
|
+
export default DashboardUpdatePanel
|
@@ -0,0 +1,151 @@
|
|
1
|
+
import { User, Users } from 'lucide-react'
|
2
|
+
import { useEffect, useState } from 'react'
|
3
|
+
|
4
|
+
import { Button } from '@/components/ui/button'
|
5
|
+
import {
|
6
|
+
Card,
|
7
|
+
CardContent,
|
8
|
+
CardDescription,
|
9
|
+
CardFooter,
|
10
|
+
CardHeader,
|
11
|
+
CardTitle,
|
12
|
+
} from '@/components/ui/card'
|
13
|
+
import { useApp } from '@/hooks/use-app'
|
14
|
+
import { DashboardDefaultData } from '@/types'
|
15
|
+
import { useNavigate } from 'react-router-dom'
|
16
|
+
|
17
|
+
const UserSummary = () => {
|
18
|
+
const navigate = useNavigate()
|
19
|
+
const { request } = useApp()
|
20
|
+
const [isRefreshing, setIsRefreshing] = useState(false)
|
21
|
+
const [data, setData] = useState<DashboardDefaultData>({
|
22
|
+
os: {
|
23
|
+
name: '',
|
24
|
+
platform: '',
|
25
|
+
version: '',
|
26
|
+
architecture: '',
|
27
|
+
uptime: 0,
|
28
|
+
cpu: {
|
29
|
+
model: '',
|
30
|
+
speed: 0,
|
31
|
+
physicalCores: 0,
|
32
|
+
virtualCores: 0,
|
33
|
+
},
|
34
|
+
memory: {
|
35
|
+
total: 0,
|
36
|
+
free: 0,
|
37
|
+
},
|
38
|
+
disk: [],
|
39
|
+
},
|
40
|
+
modules: [],
|
41
|
+
users: {
|
42
|
+
total: 0,
|
43
|
+
admin: 0,
|
44
|
+
active: 0,
|
45
|
+
activities: [],
|
46
|
+
},
|
47
|
+
database: {
|
48
|
+
connections: 0,
|
49
|
+
size: 0,
|
50
|
+
queriesPerSecond: 0,
|
51
|
+
},
|
52
|
+
} as DashboardDefaultData)
|
53
|
+
|
54
|
+
const load = async () => {
|
55
|
+
setIsRefreshing(true)
|
56
|
+
const { data } = await request<DashboardDefaultData>({
|
57
|
+
url: '/core',
|
58
|
+
})
|
59
|
+
|
60
|
+
setData(data)
|
61
|
+
|
62
|
+
setIsRefreshing(false)
|
63
|
+
}
|
64
|
+
|
65
|
+
useEffect(() => {
|
66
|
+
load()
|
67
|
+
}, [])
|
68
|
+
|
69
|
+
return (
|
70
|
+
<Card>
|
71
|
+
<CardHeader className='pb-2'>
|
72
|
+
<CardTitle className='flex items-center'>
|
73
|
+
<Users className='mr-2 h-5 w-5 text-primary' />
|
74
|
+
Resumo de Usuários
|
75
|
+
</CardTitle>
|
76
|
+
<CardDescription>Visão geral dos usuários do sistema</CardDescription>
|
77
|
+
</CardHeader>
|
78
|
+
<CardContent>
|
79
|
+
<div className='space-y-4'>
|
80
|
+
{isRefreshing ? (
|
81
|
+
<div className='grid grid-cols-3 gap-4'>
|
82
|
+
{Array.from({ length: 3 }).map((_, index) => (
|
83
|
+
<div
|
84
|
+
key={index}
|
85
|
+
className='flex animate-pulse flex-col items-center justify-center rounded-lg bg-primary/10 p-3'
|
86
|
+
>
|
87
|
+
<div className='h-6 w-12 rounded bg-muted'></div>
|
88
|
+
<div className='mt-2 h-4 w-16 rounded bg-muted'></div>
|
89
|
+
</div>
|
90
|
+
))}
|
91
|
+
</div>
|
92
|
+
) : (
|
93
|
+
<>
|
94
|
+
<div className='grid grid-cols-3 gap-4'>
|
95
|
+
<div className='flex flex-col items-center justify-center rounded-lg bg-primary/10 p-3'>
|
96
|
+
<div className='text-2xl font-bold'>{data.users.total}</div>
|
97
|
+
<div className='text-xs text-muted-foreground'>Total</div>
|
98
|
+
</div>
|
99
|
+
<div className='flex flex-col items-center justify-center rounded-lg bg-primary/10 p-3'>
|
100
|
+
<div className='text-2xl font-bold'>{data.users.admin}</div>
|
101
|
+
<div className='text-xs text-muted-foreground'>Admins</div>
|
102
|
+
</div>
|
103
|
+
<div className='flex flex-col items-center justify-center rounded-lg bg-primary/10 p-3'>
|
104
|
+
<div className='text-2xl font-bold'>{data.users.active}</div>
|
105
|
+
<div className='text-xs text-muted-foreground'>Ativos</div>
|
106
|
+
</div>
|
107
|
+
</div>
|
108
|
+
{data.users.activities.length > 0 && (
|
109
|
+
<div>
|
110
|
+
<h3 className='mb-2 text-sm font-medium'>
|
111
|
+
Atividade Recente
|
112
|
+
</h3>
|
113
|
+
|
114
|
+
<div className='space-y-2'>
|
115
|
+
{data.users.activities.map((activity, index) => (
|
116
|
+
<div
|
117
|
+
key={index}
|
118
|
+
className='flex items-center justify-between text-sm'
|
119
|
+
>
|
120
|
+
<div className='flex items-center'>
|
121
|
+
<User className='mr-2 h-4 w-4 text-muted-foreground' />
|
122
|
+
<span>{activity.user.name}</span>
|
123
|
+
</div>
|
124
|
+
<div className='text-muted-foreground'>
|
125
|
+
{activity.created_at}
|
126
|
+
</div>
|
127
|
+
</div>
|
128
|
+
))}
|
129
|
+
</div>
|
130
|
+
</div>
|
131
|
+
)}
|
132
|
+
</>
|
133
|
+
)}
|
134
|
+
</div>
|
135
|
+
</CardContent>
|
136
|
+
<CardFooter>
|
137
|
+
<Button
|
138
|
+
variant='outline'
|
139
|
+
size='sm'
|
140
|
+
className='w-full'
|
141
|
+
onClick={() => navigate('/users')}
|
142
|
+
>
|
143
|
+
<User className='mr-2 h-4 w-4' />
|
144
|
+
Ver Todos Usuários
|
145
|
+
</Button>
|
146
|
+
</CardFooter>
|
147
|
+
</Card>
|
148
|
+
)
|
149
|
+
}
|
150
|
+
|
151
|
+
export default UserSummary
|
@@ -1,11 +1,11 @@
|
|
1
|
-
{
|
2
|
-
"create": "Create dashboard",
|
3
|
-
"createText": "Fill the dashboard informations.",
|
4
|
-
"createTooltip": "Create new dashboard",
|
5
|
-
"delete": "Delete dashboard",
|
6
|
-
"deleteText": "Are you sure to delete these dashboard?",
|
7
|
-
"deleteTooltip": "Delete the selected dashboard",
|
8
|
-
"edit": "Edit dashboard",
|
9
|
-
"editText": "View and edit dashboard information.",
|
10
|
-
"editTooltip": "Edit the selected dashboard"
|
11
|
-
}
|
1
|
+
{
|
2
|
+
"create": "Create dashboard",
|
3
|
+
"createText": "Fill the dashboard informations.",
|
4
|
+
"createTooltip": "Create new dashboard",
|
5
|
+
"delete": "Delete dashboard",
|
6
|
+
"deleteText": "Are you sure to delete these dashboard?",
|
7
|
+
"deleteTooltip": "Delete the selected dashboard",
|
8
|
+
"edit": "Edit dashboard",
|
9
|
+
"editText": "View and edit dashboard information.",
|
10
|
+
"editTooltip": "Edit the selected dashboard"
|
11
|
+
}
|
@@ -1,11 +1,11 @@
|
|
1
|
-
{
|
2
|
-
"create": "Criar dashboard",
|
3
|
-
"createText": "Preencha as informações do dashboard.",
|
4
|
-
"createTooltip": "Criar novo dashboard",
|
5
|
-
"delete": "Excluir dashboard",
|
6
|
-
"deleteText": "Você tem certeza de que deseja excluir estes dashboards?",
|
7
|
-
"deleteTooltip": "Excluir o dashboard selecionado",
|
8
|
-
"edit": "Editar dashboard",
|
9
|
-
"editText": "Visualizar e editar informações do dashboard.",
|
10
|
-
"editTooltip": "Editar o dashboard selecionado"
|
11
|
-
}
|
1
|
+
{
|
2
|
+
"create": "Criar dashboard",
|
3
|
+
"createText": "Preencha as informações do dashboard.",
|
4
|
+
"createTooltip": "Criar novo dashboard",
|
5
|
+
"delete": "Excluir dashboard",
|
6
|
+
"deleteText": "Você tem certeza de que deseja excluir estes dashboards?",
|
7
|
+
"deleteTooltip": "Excluir o dashboard selecionado",
|
8
|
+
"edit": "Editar dashboard",
|
9
|
+
"editText": "Visualizar e editar informações do dashboard.",
|
10
|
+
"editTooltip": "Editar o dashboard selecionado"
|
11
|
+
}
|
@@ -1,28 +1,28 @@
|
|
1
|
-
import { useDefaultMutation } from "@/hooks/use-default-mutation";
|
2
|
-
import { useQuery } from "@tanstack/react-query";
|
3
|
-
import { requests } from "./requests";
|
4
|
-
|
5
|
-
const scope = "dashboard";
|
6
|
-
|
7
|
-
export function useDashboardCreate() {
|
8
|
-
const { dashboardCreate } = requests();
|
9
|
-
return useDefaultMutation(scope, "create", dashboardCreate);
|
10
|
-
}
|
11
|
-
|
12
|
-
export function useDashboardDelete() {
|
13
|
-
const { dashboardDelete } = requests();
|
14
|
-
return useDefaultMutation(scope, "delete", dashboardDelete);
|
15
|
-
}
|
16
|
-
|
17
|
-
export function useDashboardUpdate() {
|
18
|
-
const { dashboardUpdate } = requests();
|
19
|
-
return useDefaultMutation(scope, "update", dashboardUpdate);
|
20
|
-
}
|
21
|
-
|
22
|
-
export function useDashboardGet(id: number) {
|
23
|
-
const { dashboardGet } = requests();
|
24
|
-
return useQuery({
|
25
|
-
queryKey: [scope, "get"],
|
26
|
-
queryFn: () => dashboardGet(id),
|
27
|
-
});
|
28
|
-
}
|
1
|
+
import { useDefaultMutation } from "@/hooks/use-default-mutation";
|
2
|
+
import { useQuery } from "@tanstack/react-query";
|
3
|
+
import { requests } from "./requests";
|
4
|
+
|
5
|
+
const scope = "dashboard";
|
6
|
+
|
7
|
+
export function useDashboardCreate() {
|
8
|
+
const { dashboardCreate } = requests();
|
9
|
+
return useDefaultMutation(scope, "create", dashboardCreate);
|
10
|
+
}
|
11
|
+
|
12
|
+
export function useDashboardDelete() {
|
13
|
+
const { dashboardDelete } = requests();
|
14
|
+
return useDefaultMutation(scope, "delete", dashboardDelete);
|
15
|
+
}
|
16
|
+
|
17
|
+
export function useDashboardUpdate() {
|
18
|
+
const { dashboardUpdate } = requests();
|
19
|
+
return useDefaultMutation(scope, "update", dashboardUpdate);
|
20
|
+
}
|
21
|
+
|
22
|
+
export function useDashboardGet(id: number) {
|
23
|
+
const { dashboardGet } = requests();
|
24
|
+
return useQuery({
|
25
|
+
queryKey: [scope, "get"],
|
26
|
+
queryFn: () => dashboardGet(id),
|
27
|
+
});
|
28
|
+
}
|