diff --git a/dashboard/src/states/Dashboard/pages/Licenses/Licenses.jsx b/dashboard/src/states/Dashboard/pages/Licenses/Licenses.jsx
new file mode 100644
index 0000000..f73aa89
--- /dev/null
+++ b/dashboard/src/states/Dashboard/pages/Licenses/Licenses.jsx
@@ -0,0 +1,51 @@
+import {DataGrid} from '@mui/x-data-grid';
+import {useContext, useEffect, useState} from "react";
+import {getRequest} from "@/common/utils/RequestUtil.js";
+import {ProjectContext} from "@/states/Dashboard/contexts/Project";
+import {Button, Stack, TextField} from "@mui/material";
+import {Search} from "@mui/icons-material";
+import columns from "./columns.jsx";
+
+export const Licenses = () => {
+ const {currentProject} = useContext(ProjectContext);
+
+ const [isLoading, setIsLoading] = useState(true);
+ const [rows, setRows] = useState([]);
+ const [pageInfo, setPageInfo] = useState({totalRowCount: 0, totalPages: 0, pageSize: 100, page: 0});
+
+ const fetchLicenses = async () => {
+ setIsLoading(true);
+ try {
+ const data = await getRequest(`/license/${currentProject.id}/list?limit=${pageInfo.pageSize}&page=${pageInfo.page}`);
+
+ setRows(data?.licenses.map((license) => ({id: license.key, ...license})) || []);
+ setPageInfo(pageInfo => ({totalRowCount: data.total, totalPages: Math.ceil(data.total / pageInfo.pageSize),
+ pageSize: pageInfo.pageSize, page: pageInfo.page}));
+ setIsLoading(false);
+ } catch (e) {
+ console.error(e);
+ }
+ }
+
+ const onPageChange = async (page) => setPageInfo({...pageInfo, page: page});
+
+ useEffect(() => {
+ fetchLicenses();
+ }, [pageInfo.page]);
+
+ return (
+
+
+
+ }}/>
+
+
+
+ onPageChange(page.page)} disableColumnMenu
+ sx={{display: 'grid', gridTemplateRows: 'auto 1f auto'}} />
+
+ );
+}
\ No newline at end of file
diff --git a/dashboard/src/states/Dashboard/pages/Licenses/columns.jsx b/dashboard/src/states/Dashboard/pages/Licenses/columns.jsx
new file mode 100644
index 0000000..ec75b81
--- /dev/null
+++ b/dashboard/src/states/Dashboard/pages/Licenses/columns.jsx
@@ -0,0 +1,28 @@
+import {IconButton, Stack} from "@mui/material";
+import {Delete, Edit, Key} from "@mui/icons-material";
+
+export default [
+ {
+ field: 'key', headerName: 'License key', width: 250, renderCell: (params) =>
+ {params.value}
+
+ },
+ {field: 'groups', headerName: 'Groups', width: 200},
+ {field: 'permissions', headerName: 'Permissions', width: 200},
+ {field: 'currentUses', headerName: 'Current uses', width: 180},
+ {
+ field: 'maxUses', headerName: 'Maximum uses', width: 180, renderCell: (params) =>
+ params.value === -1 ? "Unlimited" : params.value
+ },
+ {
+ field: 'expirationDate', headerName: 'Expiration date', width: 200, renderCell: (params) =>
+ new Date(params.value).getTime() === 0 ? "Never" : new Date(params.value).toLocaleString()
+ },
+ {
+ field: 'actions', headerName: 'Actions', width: 80, renderCell: () =>
+
+
+ , sortable: false, filterable: false, align: 'center'
+ }
+];
\ No newline at end of file
diff --git a/dashboard/src/states/Dashboard/pages/Licenses/index.js b/dashboard/src/states/Dashboard/pages/Licenses/index.js
new file mode 100644
index 0000000..e9cb64d
--- /dev/null
+++ b/dashboard/src/states/Dashboard/pages/Licenses/index.js
@@ -0,0 +1 @@
+export {Licenses as default} from "./Licenses.jsx";
\ No newline at end of file