diff --git a/backend/applications.js b/backend/applications.js
new file mode 100644
index 00000000..8010750e
--- /dev/null
+++ b/backend/applications.js
@@ -0,0 +1,18 @@
+// utils/applications.js
+
+import fs from 'fs';
+import path from 'path';
+
+export function getApplications() {
+ const filePath = path.join(process.cwd(), 'content/applications/applications.json');
+ const fileContents = fs.readFileSync(filePath, 'utf8');
+ const data = JSON.parse(fileContents);
+
+ return data.applications; // Assuming the "applications" key holds the array of applications
+}
+
+export function getApplicationById(id) {
+ const applications = getApplications();
+ const application = applications.find(app => app.app_id === id);
+ return application;
+}
diff --git a/content/applications/applications.json b/content/applications/applications.json
new file mode 100644
index 00000000..4125279f
--- /dev/null
+++ b/content/applications/applications.json
@@ -0,0 +1,35 @@
+{
+ "applications": [
+ {
+ "name": "Airview",
+ "app_id": "APP1",
+ "description": "Documentation, Compliance and Control for Cloud.",
+ "environments": {
+ "development": "subscription-1234",
+ "staging": "subscription-5678",
+ "production": "subscription-9012"
+ }
+ },
+ {
+ "name": "CoolTool",
+ "app_id": "APP2",
+ "description": "A powerful tool for creative professionals.",
+ "environments": {
+ "development": "subscription-3456",
+ "staging": "subscription-7890",
+ "production": "subscription-2345"
+ }
+ },
+ {
+ "name": "InnovativeApp",
+ "app_id": "APP3",
+ "description": "A cutting-edge application with advanced features.",
+ "environments": {
+ "development": "subscription-6789",
+ "staging": "subscription-0123",
+ "production": "subscription-4567"
+ }
+ }
+ ]
+ }
+
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 0a3e6d1d..016fe4f4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -22,6 +22,7 @@
"@mui/icons-material": "^5.11.11",
"@mui/material": "^5.11.7",
"@mui/utils": "^5.13.1",
+ "@mui/x-date-pickers": "^6.5.0",
"@next/mdx": "^13.4.3",
"@sentry/nextjs": "^7.52.1",
"ahooks": "^3.7.7",
@@ -1094,6 +1095,71 @@
"react": "^17.0.0 || ^18.0.0"
}
},
+ "node_modules/@mui/x-date-pickers": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-6.5.0.tgz",
+ "integrity": "sha512-dRCO1mzHjfOqsa4LdKxiXQnV0cuGiAkliyxSDCdRn6clK2WdF9Oj+1+4Mkx7fcJA61SV1eP4Yg29s0/VDsZKZw==",
+ "dependencies": {
+ "@babel/runtime": "^7.21.0",
+ "@mui/utils": "^5.12.3",
+ "@types/react-transition-group": "^4.4.6",
+ "clsx": "^1.2.1",
+ "prop-types": "^15.8.1",
+ "react-transition-group": "^4.4.5"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mui"
+ },
+ "peerDependencies": {
+ "@emotion/react": "^11.9.0",
+ "@emotion/styled": "^11.8.1",
+ "@mui/base": "^5.0.0-alpha.87",
+ "@mui/material": "^5.8.6",
+ "@mui/system": "^5.8.0",
+ "date-fns": "^2.25.0",
+ "date-fns-jalali": "^2.13.0-0",
+ "dayjs": "^1.10.7",
+ "luxon": "^3.0.2",
+ "moment": "^2.29.4",
+ "moment-hijri": "^2.1.2",
+ "moment-jalaali": "^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0",
+ "react": "^17.0.2 || ^18.0.0",
+ "react-dom": "^17.0.2 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/react": {
+ "optional": true
+ },
+ "@emotion/styled": {
+ "optional": true
+ },
+ "date-fns": {
+ "optional": true
+ },
+ "date-fns-jalali": {
+ "optional": true
+ },
+ "dayjs": {
+ "optional": true
+ },
+ "luxon": {
+ "optional": true
+ },
+ "moment": {
+ "optional": true
+ },
+ "moment-hijri": {
+ "optional": true
+ },
+ "moment-jalaali": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@next/env": {
"version": "13.4.3",
"resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.3.tgz",
diff --git a/package.json b/package.json
index 58069209..9bb97542 100644
--- a/package.json
+++ b/package.json
@@ -25,6 +25,7 @@
"@mui/icons-material": "^5.11.11",
"@mui/material": "^5.11.7",
"@mui/utils": "^5.13.1",
+ "@mui/x-date-pickers": "^6.5.0",
"@next/mdx": "^13.4.3",
"@sentry/nextjs": "^7.52.1",
"ahooks": "^3.7.7",
diff --git a/pages/api/applications/[id].js b/pages/api/applications/[id].js
new file mode 100644
index 00000000..6d080903
--- /dev/null
+++ b/pages/api/applications/[id].js
@@ -0,0 +1,19 @@
+// pages/api/applications/[id].js
+
+import { getApplicationById } from '../../../utils/applications';
+
+export default function handler(req, res) {
+ if (req.method === 'GET') {
+ const { id } = req.query;
+ const application = getApplicationById(id);
+
+ if (application) {
+ res.status(200).json(application);
+ } else {
+ res.status(404).json({ error: 'Application not found' });
+ }
+ } else {
+ res.setHeader('Allow', ['GET']);
+ res.status(405).end(`Method ${req.method} Not Allowed`);
+ }
+}
diff --git a/pages/api/applications/index.js b/pages/api/applications/index.js
new file mode 100644
index 00000000..6ad82bab
--- /dev/null
+++ b/pages/api/applications/index.js
@@ -0,0 +1,11 @@
+import { getApplications } from '../../../backend/applications';
+
+export default function handler(req, res) {
+ if (req.method === 'GET') {
+ const applications = getApplications();
+ res.status(200).json(applications);
+ } else {
+ res.setHeader('Allow', ['GET']);
+ res.status(405).end(`Method ${req.method} Not Allowed`);
+ }
+}
diff --git a/pages/applications/[app_id].jsx b/pages/applications/[app_id].jsx
new file mode 100644
index 00000000..aa97f6e0
--- /dev/null
+++ b/pages/applications/[app_id].jsx
@@ -0,0 +1,163 @@
+import React from "react";
+
+import { Typography, Box } from "@mui/material";
+
+import Topbar from '../../components/TopBar';
+import { theme } from '../../constants/baseTheme';
+import { ThemeProvider } from '@mui/material/styles';
+import CssBaseline from '@mui/material/CssBaseline';
+import Grid from '@mui/material/Grid';
+import { getApplications, getApplicationById } from '../../backend/applications';
+
+import { ComplianceTable } from "../../components/airview-compliance-ui/features/compliance-table";
+import { ControlOverview, useControlOverviewController, } from "../../components/airview-compliance-ui/features/control-overview";
+import dynamic from 'next/dynamic'
+
+// temporary data
+import { applicationsData } from "../../components/airview-compliance-ui/stories/compliance-table/applications-data";
+import { groups, controls, resources } from "../../components/airview-compliance-ui/stories/control-overview/data";
+///
+
+export default dynamic(() => Promise.resolve(Page), {
+ ssr: false,
+ });
+
+function Page({app}) {
+ const topBarHeight = 64;
+
+ const noDataMessage={
+ title: "No issues",
+ message: "There are no issues to display for this application",
+ };
+ const invalidPermissionsMessage = {
+ title: "Notice",
+ message: "You do not have the required permissions to view the data for this application",
+ };
+
+ return (
+
+
+
+
+ {app && app.name && {app.name}}
+ {app && app.description && {app.description}}
+ {/* */}
+
+
+
+
+
+ ;
+
+ {/* */}
+
+
+
+ )
+}
+
+
+export async function getStaticPaths() {
+ let pages = [];
+ let location = 'services';
+ try {
+ const apps = await getApplications()
+ const pages = apps.map((file) => {
+ return '/applications/' + file.id
+ })
+ return {
+ fallback: true,
+ paths: pages
+ }
+ } catch (error) {
+ console.error(error)
+ return {
+ fallback: true,
+ paths: pages
+ }
+ }
+
+
+}
+export async function getStaticProps(context) {
+ try {
+ const app = await getApplicationById(context.params.app_id);
+ return {
+ props: {
+ app: app
+ },
+ };
+ } catch (error) {
+ console.error(error);
+ return {
+ props: {
+ app: {}
+ },
+ };
+ }
+}
+
+
+const ApplicationControls = () => {
+ const [state, setControlsData, setResourcesData] =
+ useControlOverviewController(() => {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ resolve(groups);
+ }, [500]);
+ });
+ }, 1);
+
+ const handleOnRequestOfControlsData = (id) => {
+ setControlsData(id, () => {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ if (id === 3) resolve("error");
+
+ resolve(controls[id]);
+ }, [500]);
+ });
+ });
+ };
+
+ const handleOnRequestOfResourcesData = (id) => {
+ setResourcesData(id, () => {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ if (id === 3) resolve("error");
+
+ resolve(resources[id]);
+ }, [500]);
+ });
+ });
+ };
+
+ const handleOnResourceExemptionDelete = () => {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ resolve();
+ }, [1000]);
+ });
+ };
+
+ const handleOnResourceExemptionSave = () => {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ resolve();
+ }, [1000]);
+ });
+ };
+
+ return (
+
+ );
+ };
+
\ No newline at end of file