使用 React 和 Recharts 为 Postgres db 创建仪表板

2025-06-04

使用 React 和 Recharts 为 Postgres db 创建仪表板

仪表板让我们能够查看数据,并让我们能够全面了解该产品或感兴趣的领域发生的所有事情。

在本教程中,我们将在 postgresSQL 数据库上构建一个仪表板。这是一个简单的仪表板,显示存储在数据库中的项目和用户视图。我们将使用它React作为前端、semantic-uiUI 库、Canonic创建 API 以及添加一些图形recharts

我们希望创建这样的东西:

最终仪表板

由于这是在 postgres 数据库上构建仪表板的指南,请确保您已准备好仪表板。

让我们深入研究一下吧!🌊

步骤 1:与 CRA 开始

我们首先创建一个新项目create-react-app



npx create-react-app postgres-dashboard


Enter fullscreen mode Exit fullscreen mode

这将为我们创建一个基本的 React 项目。我们还需要安装semantic-ui-react样式和基本的 UI 组件。



yarn add semantic-ui-react semantic-ui-css


Enter fullscreen mode Exit fullscreen mode

第 2 步:创建仪表板组件

我们将仪表板分为两部分:

  1. 顶级统计数据和图表显示在上半部分 - 命名 -DashboardGrid
  2. 表格显示在下半部分 - 命名 -DashboardTable

components在目录中的文件夹中创建这两个组件src。在每个文件夹中,我们将创建三个文件 - 一个用于 React 代码,一个用于 CSS 文件,一个用于导出组件。

DashboardGrid让我们从创建组件开始。

仪表板网格

我们创建了 4 个框并添加了样式 - 以及虚拟数据。

src/components/DashboardGrid/DashboardGrid.js



import React from "react";
import { Card, Statistic } from "semantic-ui-react";

import "./DashboardGrid.css";

const DASHBOARD_BOXES = [
  {
    title: "Total Users",
    className: "purple",
  },
  {
    title: "Total Projects",
    className: "green",
  },
  {
    title: "Projects Created",
  },
  {
    title: "Projects Completed",
  },
];

function DashboardGrid() {
  return (
    <div className="dashboardGrid">
      <div className="dashboardGrid-boxes">
        {DASHBOARD_BOXES.map((box, i) => (
          <Card className="dashboardGrid-boxes-item" centered raised>
            <Statistic
              className={box.className ? box.className : ""}
              as="h4"
              label={box.title}
              value="89"
            />
          </Card>
        ))}
      </div>
      <div>
        {/** We'll add the chat here later */}
      </div>
    </div>
  );
}

export default DashboardGrid;


Enter fullscreen mode Exit fullscreen mode

src/components/DashboardGrid/DashboardGrid.css



.dashboardGrid {
  display: flex;
  justify-content: space-between;
}

.dashboardGrid-boxes {
  display: grid;
  grid-template-rows: 1fr 1fr;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
}

.dashboardGrid-boxes-item {
  text-align: center;
  border: 2px solid #9e9e9e;
  border-radius: 4px;
  padding: 0 30px;
}

.dashboardGrid-boxes-item .value {
  font-size: 32px;
}

.dashboardGrid-boxes-item .label {
  margin-top: 6px;
  font-weight: 400;
}

.dashboardGrid-boxes-item .purple .value {
  color: #8f8cda;
}

.dashboardGrid-boxes-item .green .value {
  color: #8fcfa7;
}


Enter fullscreen mode Exit fullscreen mode

src/components/DashboardGrid/index.js



export { default } from "./DashboardGrid";


Enter fullscreen mode Exit fullscreen mode

仪表板表

与组件类似DashboardGrid,我们创建基本的表结构和相应的样式 - DashboardTable

src/components/DashboardTable/DashboardTable.js



import React from "react";
import { Table } from "semantic-ui-react";

import "./DashboardTable.css";

const TABLE_DATA = [
  {
    name: "Lorem Ipsum",
    description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
    owner: "John Doe",
    status: "in progress",
  },
];

function DashboardTable() {
  return (
    <div className="dashboardTable">
      <Table celled padded>
        <Table.Header>
          <Table.Row>
            <Table.HeaderCell>Name</Table.HeaderCell>
            <Table.HeaderCell>Description</Table.HeaderCell>
            <Table.HeaderCell>Owner</Table.HeaderCell>
            <Table.HeaderCell>Status</Table.HeaderCell>
          </Table.Row>
        </Table.Header>
        <Table.Body>
          {TABLE_DATA.map((item, i) => (
            <Table.Row>
              <Table.Cell>
                <div>{item.name}</div>
              </Table.Cell>
              <Table.Cell>
                <div>{item.description}</div>
              </Table.Cell>
              <Table.Cell>
                <div>{item.owner}</div>
              </Table.Cell>
              <Table.Cell>
                <div>{item.status}</div>
              </Table.Cell>
            </Table.Row>
          ))}
        </Table.Body>
      </Table>
    </div>
  );
}

export default DashboardTable;


Enter fullscreen mode Exit fullscreen mode

src/components/DashboardTable/DashboardTable.css



.dashboardTable {
  margin-top: 60px;
}


Enter fullscreen mode Exit fullscreen mode

src/components/DashboardTable/index.js



export { default } from "./DashboardTable";


Enter fullscreen mode Exit fullscreen mode

步骤 3:将它们连接到 App.js

让我们添加两个组件App.js并看看我们的设计是什么样子。

src/App.js



import React from "react";
import { Header } from "semantic-ui-react";

import DashboardGrid from "./components/DashboardGrid";
import DashboardTable from "./components/DashboardTable";

import "./App.css";

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <Header as="h3">Analytics Dashboard connected with Postgres</Header>
      </header>
      <div className="App-main">
        <DashboardGrid />
        <DashboardTable />
      </div>
    </div>
  );
}

export default App;


Enter fullscreen mode Exit fullscreen mode

src/App.css



.App-header {
  padding: 0 16px;
  text-align: center;
}

.App-main {
  padding: 0 16px;
  max-width: 1160px;
  margin: 60px auto 0;
}


Enter fullscreen mode Exit fullscreen mode

让我们前往终端,运行一下,yarn start看看仪表盘目前的样子。它看起来应该是这样的👇

预建

步骤 4:在 PostgreSQL 上构建 API

现在我们已经完成了仪表板的基本框架,接下来让我们在 postgresSQL 数据库上构建 API。通常,我们可以采用两种方法:

  1. 搭建连接Postgres数据库的nodejs服务器
  2. 使用开发人员工具,让我们快速创建 API

为了简短起见,我们先来看看第二种选择。我们前往Canonic为我们的后端创建一个新项目。

创建新项目→选择“链接”→选择 Postgres 并添加 Postgres Url - 看起来像 - postgres://.....

出于本指南的目的,您可以直接克隆此项目并开始 - 如果您选择此路线 - 跳过此步骤的其余部分并跳过步骤 6。

创建项目

它最终会创建链接到我们数据库的 API,并将这些 API 部署到 URL 上。您还可以在 Postgres 数据库中看到我们表的结构。

在我们当前的设置中,我们有两张表 -projectsusers。我们为这两张表提供了 CRUD API。“文档”选项卡应该包含文档。

文档

步骤5:集成DashboardTable API

现在我们有了 API,让我们将其集成到DashboardTable组件中。我们使用的axios库可以通过 进行安装yarn add axios

我们修改DashboardTable.js文件以调用我们的 API 并获取数据。



...
import axios from "axios";
import { Table, Loader, Dimmer } from "semantic-ui-react";

import "./DashboardTable.css";

// get this URL from your project on Canonic
const GET_PROJECTS_URL =
  "https://postgres-dashboard-7fc.can.canonic.dev/api/projects";

function DashboardTable() {
  const [loading, setLoading] = React.useState(false);
  const [projects, setProjects] = React.useState([]);

  React.useEffect(() => {
    setLoading(true);
    axios(GET_PROJECTS_URL).then(({ data }) => {
      setProjects(data.data || []);
      setLoading(false);
    });
  }, []);

  return (
    <div className="dashboardTable-wrapper">
      ...
        <Table.Body>
          {projects.map((item, i) => (
            <Table.Row>
              ...
                            // change item.owner to below
              <Table.Cell width={1}>
                <div>{item.users.name}</div>
              </Table.Cell>
              ...
            </Table.Row>
          ))}
        </Table.Body>
      </Table>
    </div>
  );
}

export default DashboardTable;


Enter fullscreen mode Exit fullscreen mode

步骤 6:为仪表板统计信息创建自定义 API

现在,由于我们目前只有基本的 CRUD API,因此我们必须创建自定义 GET API 以使指标显示在我们的框和图表中。

所需的指标包括 - 用户总数、项目总数、正在进行的项目数、已完成的项目数以及每天新用户/项目的分布。

让我们回到我们在 Canonic 上的项目。转到“API”选项卡,并在“项目”表中创建一个新的 API 来获取所有这些指标。填写顶层详细信息 - 标题、路径。

然后在该部分中添加以下内容outputs

API 输出

从属性面板顶部移动到代码部分,并添加以下代码。



module.exports = async function endpoint(params, ctx) {
  const [users, projects] = await Promise.all([User.find({}),Project.find({})]);
  return {
    users: users.map((user) => user.created_at),
    projects: projects.map((project) => project.created_at),
    usersCount: users.length,
    projectsCount: projects.length,
    inProgressCount: projects.filter(project => project.status === 'in progress').length,
    completedCount: projects.filter(project => project.status === 'completed').length
  }
}


Enter fullscreen mode Exit fullscreen mode

再次部署项目,我们的 API 应该就可以使用了。在“文档”选项卡中查找链接和请求参数。

步骤 7:集成 DashboardGrid API

我们将新构建的 API 集成到 DashboardGrid 组件中。



...
import axios from "axios";
import { Card, Statistic } from "semantic-ui-react";

import "./DashboardGrid.css";

const DASHBOARD_API_URL =
  "https://postgres-dashboard-7fc.can.canonic.dev/api/projects/dashboard";

function DashboardGrid() {
  const [dashboardCount, setDashboardCount] = React.useState({});

  React.useEffect(() => {
    axios(DASHBOARD_API_URL).then(({ data }) => setDashboardCount(data.data));
  }, []);

  const DASHBOARD_BOXES = [
    {
      title: "Total Users",
      className: "purple",
      value: dashboardCount?.usersCount,
    },
    {
      title: "Total Projects",
      className: "green",
      value: dashboardCount?.projectsCount,
    },
    {
      title: "In Progress",
      value: dashboardCount?.inProgressCount,
    },
    {
      title: "Completed",
      value: dashboardCount?.completedCount,
    },
  ];

  return (
    <div className="dashboardGrid">
      ...
            <Statistic
              ...
              value={box.value ? box.value : "-"}
            />
          </Card>
        ))}
      ...
    </div>
  );
}

export default DashboardGrid;


Enter fullscreen mode Exit fullscreen mode

奖励:为图表添加 Recharts!

步骤 8:安装 Recharts

recharts使用 yarn 可以轻松添加其他包。



yarn add recharts


Enter fullscreen mode Exit fullscreen mode

步骤 9:修改 DashboardGrid 以添加图表

让我们修改代码DashboardGrid并添加图表。我们将使用 AreaChart 来实现。



...
import {
  AreaChart,
  CartesianGrid,
  XAxis,
  YAxis,
  Tooltip,
  Area,
} from "recharts";

...

function DashboardGrid() {
  ...

  const { projects = [], users = [] } = dashboardCount || {};

    // We're manually making displaying the trend for this week.
  // You can always make it dynamic by using Date.now().
  // Let me know in the comments if you want me to cover this part.
  const PREVIOUS_WEEK_DATA = [
    {
      name: "30th Nov",
      projects: projects.filter(
        (created_at) =>
                // for now, we're using timestammps of the day to compare which 
                // data point lies on which day
          created_at >= 1638230400000 && created_at < 1638316799000
      ).length,
      users: users.filter(
        (created_at) =>
          created_at >= 1638230400000 && created_at < 1638316799000
      ).length,
    },
    {
      name: "1st Dec",
      projects: projects.filter(
        (created_at) =>
          created_at >= 1638316800000 && created_at < 1638403199000
      ).length,
      users: users.filter(
        (created_at) =>
          created_at >= 1638316800000 && created_at < 1638403199000
      ).length,
    },
    {
      name: "2nd Dec",
      projects: projects.filter(
        (created_at) =>
          created_at >= 1638403200000 && created_at < 1638489599000
      ).length,
      users: users.filter(
        (created_at) =>
          created_at >= 1638403200000 && created_at < 1638489599000
      ).length,
    },
    {
      name: "3rd Dec",
      projects: projects.filter(
        (created_at) =>
          created_at >= 1638489600000 && created_at < 1638575999000
      ).length,
      users: users.filter(
        (created_at) =>
          created_at >= 1638489600000 && created_at < 1638575999000
      ).length,
    },
    {
      name: "4th Dec",
      projects: projects.filter(
        (created_at) =>
          created_at >= 1638576000000 && created_at < 1638662399000
      ).length,
      users: users.filter(
        (created_at) =>
          created_at >= 1638576000000 && created_at < 1638662399000
      ).length,
    },
    {
      name: "5th Dec",
      projects: projects.filter(
        (created_at) =>
          created_at >= 1638662400000 && created_at < 1638748799000
      ).length,
      users: users.filter(
        (created_at) =>
          created_at >= 1638662400000 && created_at < 1638748799000
      ).length,
    },
  ];

  return (
    <div className="dashboardGrid">
      <div className="dashboardGrid-boxes">
        ...
      </div>
      <div>
        <div className="dashboardGrid-chart">
          New users/projects trend per day
        </div>
        <AreaChart
          width={700}
          height={250}
          data={PREVIOUS_WEEK_DATA}
          margin={{ top: 10, right: 30, left: 0, bottom: 0 }}
        >
          <defs>
            <linearGradient id="colorUv" x1="0" y1="0" x2="0" y2="1">
              <stop offset="5%" stopColor="#8884d8" stopOpacity={0.8} />
              <stop offset="95%" stopColor="#8884d8" stopOpacity={0} />
            </linearGradient>
            <linearGradient id="colorPv" x1="0" y1="0" x2="0" y2="1">
              <stop offset="5%" stopColor="#82ca9d" stopOpacity={0.8} />
              <stop offset="95%" stopColor="#82ca9d" stopOpacity={0} />
            </linearGradient>
          </defs>
          <XAxis dataKey="name" />
          <YAxis />
          <CartesianGrid strokeDasharray="3 3" />
          <Tooltip />
          <Area
            name="Projects"
            type="monotone"
            dataKey="projects"
            stroke="#8884d8"
            fillOpacity={1}
            fill="url(#colorUv)"
          />
          <Area
            name="Users"
            type="monotone"
            dataKey="users"
            stroke="#82ca9d"
            fillOpacity={1}
            fill="url(#colorPv)"
          />
        </AreaChart>
      </div>
    </div>
  );
}

export default DashboardGrid;


Enter fullscreen mode Exit fullscreen mode

终于!经过这么多修改,我们来运行一下应用,看看效果如何!应该和开头的截图一致。

恭喜!您已成功创建仪表板!🎉

GitHub 上的现场演示
示例代码

结论

希望本指南能帮助您更好地了解如何在 React 中创建仪表板、如何构建仪表板以及如何快速启动和运行一个基本的仪表板。您也可以在这里查看我们的其他指南 

加入我们的 Discord,与我们的社区讨论或分享。如有任何支持请求,请发送邮件至 support@canonic.dev。访问我们的 网站 ,了解更多关于 Canonic 的信息。

文章来源:https://dev.to/canonic/creating-a-dashboard-for-your-postgres-db-with-react-and-recharts-2ecm
PREV
除非你是一名程序员...
NEXT
如何寻找良好实践项目的想法