feat: Update admin dashboard to show registered user count and set default date range for NASA data download to current month
parent
539a5319e4
commit
a10d0e49fe
|
|
@ -1,14 +1,14 @@
|
||||||
from fastapi import APIRouter, Depends, HTTPException, status
|
from fastapi import APIRouter, Depends, HTTPException, status
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from sqlalchemy.orm import selectinload
|
from sqlalchemy.orm import selectinload
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select, func
|
||||||
from typing import List
|
from typing import List
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from app.database import get_db
|
from app.database import get_db
|
||||||
from app.models.db import User
|
from app.models.db import User
|
||||||
from app.services.auth import hash_password
|
from app.services.auth import hash_password
|
||||||
from app.services.auth_deps import get_current_user # To protect endpoints
|
from app.services.auth_deps import get_current_user, get_current_admin_user # To protect endpoints
|
||||||
|
|
||||||
router = APIRouter(prefix="/users", tags=["users"])
|
router = APIRouter(prefix="/users", tags=["users"])
|
||||||
|
|
||||||
|
|
@ -105,3 +105,16 @@ async def reset_user_password(
|
||||||
await db.commit()
|
await db.commit()
|
||||||
|
|
||||||
return {"message": f"Password for user {user.username} has been reset."}
|
return {"message": f"Password for user {user.username} has been reset."}
|
||||||
|
|
||||||
|
@router.get("/count", response_model=dict)
|
||||||
|
async def get_user_count(
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
current_admin_user: User = Depends(get_current_admin_user) # Ensure only admin can access
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Get the total count of registered users.
|
||||||
|
"""
|
||||||
|
result = await db.execute(select(func.count(User.id)))
|
||||||
|
total_users = result.scalar_one()
|
||||||
|
return {"total_users": total_users}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,33 @@
|
||||||
/**
|
/**
|
||||||
* Dashboard Page
|
* Dashboard Page
|
||||||
*/
|
*/
|
||||||
import { Card, Row, Col, Statistic } from 'antd';
|
import { Card, Row, Col, Statistic, message } from 'antd';
|
||||||
import { DatabaseOutlined, GlobalOutlined, RocketOutlined } from '@ant-design/icons';
|
import { GlobalOutlined, RocketOutlined, UserOutlined } from '@ant-design/icons';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { request } from '../../utils/request';
|
||||||
|
|
||||||
export function Dashboard() {
|
export function Dashboard() {
|
||||||
|
const [totalUsers, setTotalUsers] = useState<number | null>(null);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchUserCount = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
// Assuming '/users/count' is the new endpoint we just created in the backend
|
||||||
|
const response = await request.get('/users/count');
|
||||||
|
setTotalUsers(response.data.total_users);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch user count:', error);
|
||||||
|
message.error('无法获取用户总数');
|
||||||
|
setTotalUsers(0); // Set to 0 or handle error display
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fetchUserCount();
|
||||||
|
}, []); // Run once on mount
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h1>控制台</h1>
|
<h1>控制台</h1>
|
||||||
|
|
@ -13,7 +36,7 @@ export function Dashboard() {
|
||||||
<Card>
|
<Card>
|
||||||
<Statistic
|
<Statistic
|
||||||
title="天体总数"
|
title="天体总数"
|
||||||
value={18}
|
value={18} // Currently hardcoded
|
||||||
prefix={<GlobalOutlined />}
|
prefix={<GlobalOutlined />}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
@ -22,7 +45,7 @@ export function Dashboard() {
|
||||||
<Card>
|
<Card>
|
||||||
<Statistic
|
<Statistic
|
||||||
title="探测器"
|
title="探测器"
|
||||||
value={7}
|
value={7} // Currently hardcoded
|
||||||
prefix={<RocketOutlined />}
|
prefix={<RocketOutlined />}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
@ -30,13 +53,14 @@ export function Dashboard() {
|
||||||
<Col span={8}>
|
<Col span={8}>
|
||||||
<Card>
|
<Card>
|
||||||
<Statistic
|
<Statistic
|
||||||
title="数据记录"
|
title="注册用户数"
|
||||||
value={1245}
|
value={totalUsers !== null ? totalUsers : '-'}
|
||||||
prefix={<DatabaseOutlined />}
|
loading={loading}
|
||||||
|
prefix={<UserOutlined />}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -56,8 +56,8 @@ export function NASADownload() {
|
||||||
const [bodies, setBodies] = useState<GroupedBodies>({});
|
const [bodies, setBodies] = useState<GroupedBodies>({});
|
||||||
const [selectedBodies, setSelectedBodies] = useState<string[]>([]);
|
const [selectedBodies, setSelectedBodies] = useState<string[]>([]);
|
||||||
const [dateRange, setDateRange] = useState<[Dayjs, Dayjs]>([
|
const [dateRange, setDateRange] = useState<[Dayjs, Dayjs]>([
|
||||||
dayjs().subtract(1, 'month').startOf('month'),
|
dayjs().startOf('month'),
|
||||||
dayjs().subtract(1, 'month').endOf('month')
|
dayjs().endOf('month')
|
||||||
]);
|
]);
|
||||||
const [availableDates, setAvailableDates] = useState<Set<string>>(new Set());
|
const [availableDates, setAvailableDates] = useState<Set<string>>(new Set());
|
||||||
const [loadingDates, setLoadingDates] = useState(false);
|
const [loadingDates, setLoadingDates] = useState(false);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue