diff --git a/.babelrc b/.babelrc deleted file mode 100644 index 34abf3ff..00000000 --- a/.babelrc +++ /dev/null @@ -1,4 +0,0 @@ -{ - "presets": ["next/babel"], - "plugins": ["styled-components"] -} diff --git a/.devcontainer.json b/.devcontainer.json deleted file mode 100644 index 630d7e9e..00000000 --- a/.devcontainer.json +++ /dev/null @@ -1,34 +0,0 @@ -// See https://aka.ms/vscode-remote/devcontainer.json for format details or -// https://aka.ms/vscode-dev-containers/definitions for sample configurations. -{ - "name": "Airview Next", - "dockerFile": "Dockerfile.vscode", - "remoteUser": "vscode", - "mounts": [ - "source=/home/ubuntu/vscode/.ssh,target=/home/vscode/.ssh,type=bind,consistency=cached" - ], - "forwardPorts": [8080], - - "customizations": { - "codespaces": { - "openFiles": ["README.md", "src/pages/presentation.mdx"] - }, - "vscode": { - "extensions": [ - "GitHub.github-vscode-theme", - "GitHub.vscode-pull-request-github" - ] - } - }, - - "portsAttributes": { - "8080": { - "label": "LocalDev", - "onAutoForward": "openPreview" - } - }, - "postCreateCommand": "", - "postAttachCommand": { - "fix npm" : "rm -Rf /workspaces/airview-next/node_modules && ln -s /tmp/npm/node_modules /workspaces/airview-next/node_modules" - } -} diff --git a/Dockerfile.vscode b/.devcontainer/Dockerfile.vscode similarity index 69% rename from Dockerfile.vscode rename to .devcontainer/Dockerfile.vscode index 1ce129d6..29473615 100644 --- a/Dockerfile.vscode +++ b/.devcontainer/Dockerfile.vscode @@ -1,4 +1,4 @@ -FROM node:18.18-alpine +FROM node:lts-alpine ARG DEBIAN_FRONTEND=noninteractive ARG USERNAME=vscode ARG USER_UID=1000 @@ -18,13 +18,13 @@ RUN apk add doas git github-cli; \ RUN git config --global url."git@github.com:".insteadOf "https://github.com/" RUN git config --global url."git@ssh.dev.azure.com:".insteadOf "https://ssh.dev.azure.com/" -## fix npm file issues -RUN mkdir /tmp/npm \ -&& mkdir /tmp/npm-cache \ -&& npm config set prefix /tmp/npm \ -&& npm config set cache /tmp/npm-cache \ -&& chown -R $USERNAME /tmp/npm \ -&& mkdir /workspaces && chown -R $USERNAME /workspaces +# ## fix npm file issues +# RUN mkdir /tmp/npm \ +# && mkdir /tmp/npm-cache \ +# && npm config set prefix /tmp/npm \ +# && npm config set cache /tmp/npm-cache \ +# && chown -R $USERNAME /tmp/npm \ +# && mkdir /workspaces && chown -R $USERNAME /workspaces ## fix M1 pupetteer issues ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD true ENV PUPPETEER_EXECUTABLE_PATH /usr/bin/chromium @@ -32,12 +32,12 @@ ENV PUPPETEER_EXECUTABLE_PATH /usr/bin/chromium # [Optional] Set the default user. Omit if you want to keep the default as root. USER $USERNAME -COPY . /tmp/npm +# COPY . /tmp/npm -RUN doas chown -R $USERNAME /tmp/npm -RUN npm config set prefix /tmp/npm -RUN yarn config set prefix /tmp/npm -RUN cd /tmp/npm && npm i +# RUN doas chown -R $USERNAME /tmp/npm +# RUN npm config set prefix /tmp/npm +# RUN yarn config set prefix /tmp/npm +# RUN npm i # RUN git clone https://github.com/AirWalk-Digital/airview-mdx.git /tmp/airview-mdx && cd /tmp/airview-mdx && npm i && npm link diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..bfc98302 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,44 @@ +// See https://aka.ms/vscode-remote/devcontainer.json for format details or +// https://aka.ms/vscode-dev-containers/definitions for sample configurations. +{ + "name": "Airview Next", + "dockerFile": "Dockerfile.vscode", + "remoteUser": "vscode", + //"mounts": [ + // "source=/Users/robe/.ssh,target=/home/vscode/.ssh,type=bind,consistency=cached" + //], + "forwardPorts": [3000, 6006], + "portsAttributes": { + "3000": { + "label": "Next JS", + "onAutoForward": "silent" + }, + "6006": { + "label": "Storybook", + "onAutoForward": "silent" + } + }, + // "postCreateCommand": "", + "postCreateCommand": { + // "fix npm" : "rm -Rf /workspaces/airview-next/node_modules && ln -s /tmp/npm/node_modules /workspaces/airview-next/node_modules" + "install modules": "cd ${containerWorkspaceFolder} && npm i" + }, + "customizations": { + "vscode": { + "extensions": [ + "dbaeumer.vscode-eslint", + "mikestead.dotenv", + "csstools.postcss", + "bradlc.vscode-tailwindcss", + "Orta.vscode-jest", + "humao.rest-client", + "yoavbls.pretty-ts-errors", + "ms-playwright.playwright", + "github.vscode-github-actions", + "lokalise.i18n-ally", + "GitHub.copilot", + "esbenp.prettier-vscode" + ] + } + } +} diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..7c53e6f6 --- /dev/null +++ b/.env.example @@ -0,0 +1,49 @@ + +# Etherpad +ETHERPAD_API_KEY= +ETHERPAD_BASE_URL= +ETHERPAD_URL= + +# FontAwesome +FONTAWESOME_NPM_AUTH_TOKEN= + +# Github +GITHUB_REPO_NAME= +GITHUB_ORG_NAME= +GITHUB_INSTALLATION_ID= +GITHUB_APP_ID= +GITHUB_PRIVATE_KEY_FILE= + +# Redis +REDIS_HOST=172.17.0.1 +REDIS_PASSWORD= +REDIS_PORT= +INDEX_NAME= + +# SharePoint +SHAREPOINT_CLIENT_ID= +SHAREPOINT_TENANT= +SHAREPOINT_PRIVATE_KEY_FILE= +SHAREPOINT_PRIVATE_KEY_THUMBPRINT= +SHAREPOINT_BASE= + +# Timesheet Portal +TSP_CLIENT_ID= +TSP_CLIENT_SECRET= + +# Microsoft Graph +AZURE_APP_CLIENT_ID= +AZURE_APP_CLIENT_SECRET= +AZURE_TENANT_ID= + +# OpenAI +OPENAI_API_KEY= +MODEL_TEMPERATURE=0.8 + +# Sentry.io +# SENTRY_AUTH_TOKEN= + +# Langchain +SIMILARITY_THRESHOLD=0.8 +# LANGCHAIN_API_KEY= +# LANGCHAIN_PROJECT= \ No newline at end of file diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..b99df67d --- /dev/null +++ b/.eslintignore @@ -0,0 +1,7 @@ +node_modules +out +docs +.storybook +.archive +.next +storybook-static \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 00000000..54457d09 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,119 @@ +{ + // Configuration for JavaScript files + "extends": [ + "airbnb-base", + "next/core-web-vitals", // Needed to avoid warning in next.js build: 'The Next.js plugin was not detected in your ESLint configuration' + "plugin:prettier/recommended" + ], + "rules": { + "prettier/prettier": [ + "error", + { + "singleQuote": true, + "endOfLine": "auto" + } + ], // Avoid conflict rule between Prettier and Airbnb Eslint + "import/extensions": [ + "error", + "ignorePackages", + { + "js": "never", + "jsx": "never", + "ts": "never", + "tsx": "never" + } + ] + }, + "settings": { + "import/resolver": { + "node": { + "extensions": [".js", ".jsx", ".ts", ".tsx"] + } + } + }, + "overrides": [ + // Configuration for TypeScript files + { + "files": ["**/*.ts", "**/*.tsx"], + "plugins": [ + "@typescript-eslint", + "unused-imports", + "tailwindcss", + "simple-import-sort" + ], + "extends": [ + "plugin:tailwindcss/recommended", + "airbnb", + "airbnb-typescript", + "next/core-web-vitals", + "plugin:prettier/recommended" + ], + "parserOptions": { + "project": "./tsconfig.json" + }, + "rules": { + "prettier/prettier": [ + "error", + { + "singleQuote": true, + "endOfLine": "auto" + } + ], // Avoid conflict rule between Prettier and Airbnb Eslint + "import/extensions": "off", // Avoid missing file extension errors, TypeScript already provides a similar feature + "react/function-component-definition": "off", // Disable Airbnb's specific function type + "react/destructuring-assignment": "off", // Vscode doesn't support automatically destructuring, it's a pain to add a new variable + "react/require-default-props": "off", // Allow non-defined react props as undefined + "react/jsx-props-no-spreading": "off", // _app.tsx uses spread operator and also, react-hook-form + "@typescript-eslint/comma-dangle": "off", // Avoid conflict rule between Eslint and Prettier + "@typescript-eslint/consistent-type-imports": "error", // Ensure `import type` is used when it's necessary + "no-restricted-syntax": [ + "error", + "ForInStatement", + "LabeledStatement", + "WithStatement" + ], // Overrides Airbnb configuration and enable no-restricted-syntax + "import/prefer-default-export": "off", // Named export is easier to refactor automatically + "simple-import-sort/imports": "error", // Import configuration for `eslint-plugin-simple-import-sort` + "simple-import-sort/exports": "error", // Export configuration for `eslint-plugin-simple-import-sort` + "import/order": "off", // Avoid conflict rule between `eslint-plugin-import` and `eslint-plugin-simple-import-sort` + "@typescript-eslint/no-unused-vars": "off", + "unused-imports/no-unused-imports": "error", + "unused-imports/no-unused-vars": [ + "error", + { + "argsIgnorePattern": "^_" + } + ] + } + }, + // Configuration for testing + { + "files": ["**/*.test.ts", "**/*.test.tsx"], + "plugins": ["jest", "jest-formatting", "testing-library", "jest-dom"], + "extends": [ + "plugin:jest/recommended", + "plugin:jest-formatting/recommended", + "plugin:testing-library/react", + "plugin:jest-dom/recommended" + ] + }, + // Configuration for e2e testing (Playwright) + { + "files": ["**/*.spec.ts"], + "extends": ["plugin:playwright/recommended"] + }, + // Configuration for Storybook + { + "files": ["*.stories.*"], + "extends": ["plugin:storybook/recommended"], + "rules": { + "import/no-extraneous-dependencies": [ + "error", + { + "devDependencies": true + } + ] + } + } + ] +} diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..f17f51b2 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" + diff --git a/.github/workflows/auto-build.yml b/.github/workflows/auto-build.yml index ce9b9da2..4dd08e0d 100644 --- a/.github/workflows/auto-build.yml +++ b/.github/workflows/auto-build.yml @@ -23,7 +23,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v1 with: - node-version: 18.x + node-version: 20.x - name: Install semantic-release extra plugins run: mv package.json package.json.bak && npm install --no-save @semantic-release/changelog @semantic-release-plus/docker && mv package.json.bak package.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..5336cfae --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,75 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + build: + strategy: + matrix: + node-version: [20.x] + # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ + + name: Build with ${{ matrix.node-version }} + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + cache: "npm" + - run: npm ci + - run: npm run build + + test: + strategy: + matrix: + node-version: [20.x] + + name: Run all tests + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 # Retrieve Git history, needed to verify commits + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + cache: "npm" + - run: npm ci + + - name: Build Next.js for E2E tests + run: npm run build + + - if: github.event_name == 'pull_request' + name: Validate all commits from PR + run: npx commitlint --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }} --verbose + + - name: Linter + run: npm run lint + + - name: Type checking + run: npm run check-types + + - name: Run unit tests + run: npm run test + + - name: Install Playwright (used for Storybook and E2E tests) + run: npx playwright install --with-deps + + - name: Run storybook tests + run: npm run test-storybook:ci + + # - name: Run E2E tests + # run: npx percy exec -- npm run test:e2e + # env: + # PERCY_TOKEN: ${{ secrets.PERCY_TOKEN }} + + \ No newline at end of file diff --git a/.github/workflows/storybook.yml b/.github/workflows/storybook.yml new file mode 100644 index 00000000..025d634a --- /dev/null +++ b/.github/workflows/storybook.yml @@ -0,0 +1,29 @@ +name: Build and Deploy Storybook +on: + push: + branches: + - "main" +jobs: + build-and-deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout 🛎️ + uses: actions/checkout@v2.3.1 + with: + persist-credentials: false + - name: Install and Build 🔧 + run: | # Install npm packages and build the Storybook files + npm config set "@fortawesome:registry" https://npm.fontawesome.com/ + npm config set "//npm.fontawesome.com/:_authToken" $FONTAWESOME_NPM_AUTH_TOKEN + npm install + npm run build-storybook + env: + FONTAWESOME_NPM_AUTH_TOKEN: ${{ secrets.FONTAWESOME_NPM_AUTH_TOKEN }} + - name: Deploy 🚀 + uses: JamesIves/github-pages-deploy-action@3.6.2 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BRANCH: gh-pages # The branch the action should deploy to. + FOLDER: storybook-static # The folder that the build-storybook script generates files. + CLEAN: true # Automatically remove deleted files from the deploy branch + TARGET_FOLDER: docs # The folder that we serve our Storybook files from \ No newline at end of file diff --git a/.gitignore b/.gitignore index bae99c05..e9012e57 100644 --- a/.gitignore +++ b/.gitignore @@ -43,4 +43,6 @@ yarn-error.log* # Temp stuff components/dashboard-demo/ /.env -components.old/ \ No newline at end of file +.archive/ + +app/api/chat/chroma \ No newline at end of file diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100755 index 00000000..0bdcce4b --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1,2 @@ +#!/bin/sh +cd "$(dirname "$0")/.." && npx --no -- commitlint --edit $1 diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 00000000..644968e8 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,3 @@ +#!/bin/sh +# Disable concurent to run `check-types` after ESLint in lint-staged +cd "$(dirname "$0")/.." && npx lint-staged --concurrent false diff --git a/.node-version b/.node-version deleted file mode 100644 index 741b4916..00000000 --- a/.node-version +++ /dev/null @@ -1 +0,0 @@ -18.14.0 \ No newline at end of file diff --git a/.nvmrc b/.nvmrc deleted file mode 100644 index e6db45a9..00000000 --- a/.nvmrc +++ /dev/null @@ -1 +0,0 @@ -18.14.0 diff --git a/.storybook/main.ts b/.storybook/main.ts new file mode 100644 index 00000000..6f641ec9 --- /dev/null +++ b/.storybook/main.ts @@ -0,0 +1,37 @@ +import type { StorybookConfig } from "@storybook/nextjs"; +import path from "path"; + +const config: StorybookConfig = { + stories: ["../src/**/*.mdx", "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"], + addons: [ + // "@storybook/addon-onboarding", + "@storybook/addon-links", + "@storybook/addon-essentials", + // "@chromatic-com/storybook", + "@storybook/addon-interactions", + // "@storybook/addon-mdx-gfm" + ], + framework: { + name: "@storybook/nextjs", + options: {}, + }, + docs: { + autodocs: "tag", + }, + staticDirs: ["../public"], + webpackFinal: async (config) => { + if (config.resolve) { + config.resolve.alias = { + ...config.resolve.alias, + '@/lib': path.resolve(__dirname, "../src/lib"), + '@/components': path.resolve(__dirname, "../src/_components"), + '@/styles': path.resolve(__dirname, "../src/_styles"), + '@/features': path.resolve(__dirname, "../src/_features"), + }; + } + + return config; + }, + core: {disableWhatsNewNotifications: true} +}; +export default config; diff --git a/.storybook/manager.ts b/.storybook/manager.ts new file mode 100644 index 00000000..08f8cae7 --- /dev/null +++ b/.storybook/manager.ts @@ -0,0 +1,6 @@ +import { addons } from '@storybook/manager-api'; +import customTheme from './theme.js'; + +addons.setConfig({ + theme: customTheme, +}); \ No newline at end of file diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx new file mode 100644 index 00000000..064dbc6f --- /dev/null +++ b/.storybook/preview.tsx @@ -0,0 +1,32 @@ +import React from "react"; +import type { Preview } from "@storybook/react"; +import CssBaseline from "@mui/material/CssBaseline"; +import { ThemeProvider } from "@mui/material/styles"; +import { AppRouterCacheProvider } from "@mui/material-nextjs/v14-appRouter"; +// import { baseTheme } from '@/styles/baseTheme'; +import { baseTheme } from "../src/_styles/baseTheme"; +// import theme from './theme'; + +const preview: Preview = { + parameters: { + controls: { + matchers: { + color: /(background|color)$/i, + date: /Date$/i, + }, + }, + }, + decorators: [ + (Story) => ( + + + {/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */} + + + + + ), + ], +}; + +export default preview; diff --git a/.storybook/theme.js b/.storybook/theme.js new file mode 100644 index 00000000..bb44c6c7 --- /dev/null +++ b/.storybook/theme.js @@ -0,0 +1,9 @@ +import { create } from '@storybook/theming/create'; + +export default create({ + base: 'light', + brandTitle: 'Airview Storybook', + brandUrl: 'https://airwalkconsulting.io', + brandImage: '/logos/airwalk-logo.png', + brandTarget: '_self', +}); \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..daa2a0a6 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,14 @@ +{ + "recommendations": [ + "dbaeumer.vscode-eslint", + "mikestead.dotenv", + "csstools.postcss", + "bradlc.vscode-tailwindcss", + "Orta.vscode-jest", + "humao.rest-client", + "yoavbls.pretty-ts-errors", + "ms-playwright.playwright", + "github.vscode-github-actions", + "lokalise.i18n-ally" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..ebbe2058 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,19 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Next.js: debug full stack", + "type": "node-terminal", + "request": "launch", + "command": "npm run dev", + "serverReadyAction": { + "pattern": "- Local:.+(https?://.+)", + "uriFormat": "%s", + "action": "debugWithChrome" + } + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 77e6f216..fd2c0124 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,38 @@ { - "typescript.tsdk": "../../tmp/npm/node_modules/typescript/lib", - "typescript.enablePromptUseWorkspaceTsdk": true -} \ No newline at end of file + "editor.tabSize": 2, + "editor.detectIndentation": false, + "search.exclude": { + "package-lock.json": true + }, + "editor.defaultFormatter": "dbaeumer.vscode-eslint", + "editor.formatOnSave": false, + "editor.codeActionsOnSave": [ + "source.addMissingImports", + "source.fixAll.eslint" + ], + "typescript.tsdk": "node_modules/typescript/lib", // Use the workspace version of TypeScript + "typescript.enablePromptUseWorkspaceTsdk": true, // For security reasons it's require that users opt into using the workspace version of typescript + "typescript.preferences.autoImportFileExcludePatterns": [ + // useRouter should be imported from `next/navigation` instead of `next/router` + "next/router.d.ts", + "next/dist/client/router.d.ts" + ], + "typescript.preferences.preferTypeOnlyAutoImports": true, // Prefer type-only imports + "jest.autoRun": { + "watch": false // Start the jest with the watch flag + // "onStartup": ["all-tests"] // Run all tests upon project launch + }, + "jest.showCoverageOnLoad": true, // Show code coverage when the project is launched + "jest.autoRevealOutput": "on-exec-error", // Don't automatically open test explorer terminal on launch + // Multiple language settings for json and jsonc files + "[json][jsonc][yaml]": { + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "prettier.ignorePath": ".gitignore", // Don't run prettier for files listed in .gitignore + "i18n-ally.localesPaths": ["src/locales"], + "i18n-ally.keystyle": "nested", + "[jsonc]": { + "editor.defaultFormatter": "vscode.json-language-features" + } +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..f02b913a --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,21 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "Project wide type checking with TypeScript", + "type": "npm", + "script": "check-types", + "problemMatcher": ["$tsc"], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "clear": true, + "reveal": "never" + } + } + ] +} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 457190ec..eb945c6c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,10 +7,10 @@ Please note we have a code of conduct (see below), please follow it in all your ## Setup -1. Clone project `git clone git@github.com:whoisryosuke/next-mdx-deck.git` -1. Navigate to project `cd next-mdx-deck` -1. Install dependencies `yarn` -1. Run the development server `yarn dev` +1. Clone project `git clone xxxx` +1. Navigate to project `cd xxk` +1. Install dependencies `npm` +1. Run the development server `npm run dev` ## Development @@ -21,79 +21,3 @@ See README.md for information on working with project. 1. Make a PR with working or conceptual code. 1. Submit it. -> That's it. I'd say more if we had tests, a CI/CD setup, versioning, etc. But it's pretty casual right now. Make sure if you have working code to test the build process and ensure it doesn't fail. - -## Code of Conduct - -### Our Pledge - -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, gender identity and expression, level of experience, -nationality, personal appearance, race, religion, or sexual identity and -orientation. - -### Our Standards - -Examples of behavior that contributes to creating a positive environment -include: - -- Using welcoming and inclusive language -- Being respectful of differing viewpoints and experiences -- Gracefully accepting constructive criticism -- Focusing on what is best for the community -- Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -- The use of sexualized language or imagery and unwelcome sexual attention or - advances -- Trolling, insulting/derogatory comments, and personal or political attacks -- Public or private harassment -- Publishing others' private information, such as a physical or electronic - address, without explicit permission -- Other conduct which could reasonably be considered inappropriate in a - professional setting - -### Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or -reject comments, commits, code, wiki edits, issues, and other contributions -that are not aligned to this Code of Conduct, or to ban temporarily or -permanently any contributor for other behaviors that they deem inappropriate, -threatening, offensive, or harmful. - -### Scope - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. - -### Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at [INSERT EMAIL ADDRESS]. All -complaints will be reviewed and investigated and will result in a response that -is deemed necessary and appropriate to the circumstances. The project team is -obligated to maintain confidentiality with regard to the reporter of an incident. -Further details of specific enforcement policies may be posted separately. - -Project maintainers who do not follow or enforce the Code of Conduct in good -faith may face temporary or permanent repercussions as determined by other -members of the project's leadership. - -### Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at [http://contributor-covenant.org/version/1/4][version] - -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ diff --git a/README.md b/README.md index 1a712a66..d0451f54 100644 --- a/README.md +++ b/README.md @@ -1,57 +1,19 @@ # Next Airview -Create presentation decks using MDX, React, and [Next.js](https://nextjs.org/). - ## Features - +- 📝 Documentation as Code (via Github) +- ✍️ Collaborative editing (via Etherpad) - 📽 React-based Slideshow - ✍️ Write using Markdown, React components, even HTML! -- 🎨 Themeable with CSS -- 👉 Swipe to change slides -- ♻️ Sync slides between browser tabs -- 👨‍💻 Presentation Mode -- 📝 Speaker Notes -- ✍️ MDX mode for use within a website (Airview) ## Getting Started 1. Clone the project: `git clone https://github.com/AirWalk-Digital/airview-next` -2. Install dependencies: `yarn` -3. Run the dev server: `npm run dev` or `yarn dev` -4. Edit the first slide in `/markdwon/test.mdx` and save to [**see changes**](http://localhost:3000/files/ppt/test.mdx)! - -*or* - -1. Browse to Etherpad [here](https://pad.airview.airwalkconsulting.io) -2. Create a new pad -3. [**browse pads**](http://localhost:3000/ - -*or* - -use the docker image with Etherpad: - -```bash -source .env* && docker run -d --env ETHERPAD_API_KEY=$ETHERPAD_API_KEY --env ETHERPAD_BASE_URL=$ETHERPAD_BASE_URL -p 8080:3000 --name mdx-deck ghcr.io/airwalk-digital/airview-next:local -``` - -[**browse pads**](http://localhost:8080/ - - -When you're done, run `npm run build && npm run export` or `yarn build && yarn export` will create a static app you can deploy anywhere (or use locally). See below for more details. - -### Deploying - -This project is easy to build locally or using a host with build services (like Netlify or Now). - -1. ⚙️ Run the build process: `yarn export` -1. 🚀 Upload the static contents of `out` folder to host/CDN (or run the `out/index.html` locally) - -## How to Use - -### 💬 Changing the Title/Date/etc - -The default theme includes a title, date, author (and link to the author's website) in of the `
` component. You can edit this data inside the `site.config.js` file. +2. in VScode - "Open in Container" using remote containers. +3. Install dependencies: `npm i` +4. Run the dev server: `npm run dev` +5. Run storybook: `npm run storybook` ### ✍️ Writing JSX @@ -64,71 +26,6 @@ You can use JSX in [a few ways](https://mdxjs.com/getting-started) in your MDX f [Check out the MDX docs](https://mdxjs.com/getting-started) for more information on the syntax. -### 📃 Creating Slide Pages - -You can create new slide pages by making new `.mdx` files inside the `/markdown/` directory. Each slide is separated by a markdown divider (`---`). - -> **You have to wrap the contents of each `.mdx` file with a `` component.** This is what breaks up each slide page into individual slides using the markdown dividers. [Note: this isn't required now] - -### 🗺 Navigation - -[Note: this isn't implemented yet] Slide pages represent real pages - so `page-2.mdx` == `your-site.com/page-2`. By using the `next` prop on the `` component you can control the next page you navigate to (after you go through all the slides). - -```js -// Navigates to your-site.com/your-custom-page after all slides complete - -``` - -### 🎨 Theming the Slideshow - -Theming is accomplished with **CSS custom properties** and/or **Styled Components**. - -Design tokens are stored as CSS custom properties inside the SlidePage layout (`/layouts/SlidePage.jsx`), which are injected into the app using Styled Component's global styling utility. There you can change the color of text, background colors, fonts, etc. - -The actual CSS styles of the Slideshow are also stored in the SlidePage layout. There you can change the padding of slides, alignment of quotes, etc. - -When the Markdown is parsed into HTML, you can replace HTML with React components. These "swaps" are handled by the `` component. You can import custom components and swap elements (like a ` - ); -} - -function useStyles() { - return { - button: { - fontSize: "12px", - fontWeight: "bold", - backgroundColor: "#fff", - color: "#000", - "&:hover": { - backgroundColor: "#fff", - }, - minWidth: 50, - paddingTop: "2px", - paddingBottom: "2px", - paddingRight: "9px", - paddingLeft: "9px", - }, - }; -} - -ApplicationTileCallToActionButton.propTypes = { - label: PropTypes.string.isRequired, - classNames: PropTypes.string, - component: PropTypes.any, - linkProps: PropTypes.object, -}; diff --git a/components/airview-compliance-ui/features/application-tile/application-tile-chip.js b/components/airview-compliance-ui/features/application-tile/application-tile-chip.js deleted file mode 100644 index f5ecc849..00000000 --- a/components/airview-compliance-ui/features/application-tile/application-tile-chip.js +++ /dev/null @@ -1,40 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { Tooltip } from "@mui/material"; -import { IconChip } from "../icon-chip/icon-chip"; - -export function ApplicationTileChip({ - tooltipLabel, - icon, - label, - color, - ...rest -}) { - return ( - - - - ); -} - -// function useStyles() { -// return{ -// root: { -// "&:hover": { -// cursor: "default", -// }, -// }, -// } -// }; - -ApplicationTileChip.propTypes = { - tooltipLabel: PropTypes.string.isRequired, - icon: PropTypes.node.isRequired, - label: PropTypes.string.isRequired, - color: PropTypes.string.isRequired, -}; diff --git a/components/airview-compliance-ui/features/application-tile/application-tile-content-row.js b/components/airview-compliance-ui/features/application-tile/application-tile-content-row.js deleted file mode 100644 index c594e18c..00000000 --- a/components/airview-compliance-ui/features/application-tile/application-tile-content-row.js +++ /dev/null @@ -1,59 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { Box } from "@mui/material"; - -export function ApplicationTileContentRow({ - children, - inlineContent = false, - classNames, -}) { - const classes = useApplicationTileContentRowStyles(); - - return ( - - {children} - - ); -} - -ApplicationTileContentRow.propTypes = { - /** - * Supported sub-components to create the ApplicationTile composition - */ - children: PropTypes.node, - /** - * Modifies the layout properties of child nodes when using inline content, for example `IconChip` components - */ - inlineContent: PropTypes.bool, - /** - * Allows the passing of additional style classes to the component root node - */ - classNames: PropTypes.string, -}; - -function useApplicationTileContentRowStyles() { - return { - inlineContent: { - "&:last-of-type": { - marginBottom: "-12px", - }, - - "& > *": { - marginBottom: 1.5, - }, - - "& > *:not(:last-child)": { - marginRight: 1.5, - }, - }, - - root: { - "& > *:not(:last-child)": { - marginBottom: 1.5, - }, - }, - }; -} diff --git a/components/airview-compliance-ui/features/application-tile/application-tile-content.js b/components/airview-compliance-ui/features/application-tile/application-tile-content.js deleted file mode 100644 index bf7f10c1..00000000 --- a/components/airview-compliance-ui/features/application-tile/application-tile-content.js +++ /dev/null @@ -1,80 +0,0 @@ -import React, { useState, useCallback } from "react"; -import PropTypes from "prop-types"; -import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; -import ExpandLessIcon from "@mui/icons-material/ExpandLess"; -import { IconButton, Collapse, Box } from "@mui/material"; - -export function ApplicationTileContent({ - children, - collapsible = false, - initialCollapsed = false, - classNames, -}) { - const classes = useApplicationTileContentStyles(collapsible); - - const [collapsed, setCollapsed] = useState(initialCollapsed); - - const handleOnToggleCollapsed = useCallback(() => { - setCollapsed(!collapsed); - }, [collapsed]); - - return ( - - {collapsible && ( - - {collapsed ? : } - - )} - - {children} - - - ); -} - -ApplicationTileContent.propTypes = { - /** - * Supported sub-components to create the ApplicationTile composition - */ - children: PropTypes.node, - /** - * Sets the content to enable collapsible content toggling - */ - collapsible: PropTypes.bool, - /** - * Sets the initial collapsible state (if collapsible prop is true) - */ - initialCollapsed: PropTypes.bool, - /** - * Allows the passing of additional style classes to the component root node - */ - classNames: PropTypes.string, -}; - -function useApplicationTileContentStyles(collapsible) { - return { - root: { - padding: 2, - }, - collapseToggleBtn: { - display: "block", - margin: "0 auto", - padding: 0, - }, - collapseWrapperInner: { - "& .MuiCollapse-wrapperInner": { - paddingTop: collapsible ? 2 : 0, - }, - }, - }; -} diff --git a/components/airview-compliance-ui/features/application-tile/application-tile-divider.js b/components/airview-compliance-ui/features/application-tile/application-tile-divider.js deleted file mode 100644 index 6448b680..00000000 --- a/components/airview-compliance-ui/features/application-tile/application-tile-divider.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { Divider } from "@mui/material"; - -export function ApplicationTileDivider({ classNames }) { - return ; -} - -ApplicationTileDivider.propTypes = { - /** - * Allows the passing of additional style classes to the component root node - */ - classNames: PropTypes.string, -}; diff --git a/components/airview-compliance-ui/features/application-tile/application-tile-header.js b/components/airview-compliance-ui/features/application-tile/application-tile-header.js deleted file mode 100644 index d2ee505a..00000000 --- a/components/airview-compliance-ui/features/application-tile/application-tile-header.js +++ /dev/null @@ -1,78 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { Box } from "@mui/material"; - -export function ApplicationTileHeader({ - leftContent, - rightContent, - dense = false, - classNames, -}) { - const classes = useApplicationTileHeaderStyles(dense); - return ( - - {leftContent && ( - - {leftContent} - - )} - - {rightContent && ( - - {rightContent} - - )} - - ); -} - -ApplicationTileHeader.propTypes = { - /** - * Left aligned child content - */ - leftContent: PropTypes.node, - /** - * Right aligned child content - */ - rightContent: PropTypes.node, - /** - * Reduces the padding of the header container - */ - dense: PropTypes.bool, - /** - * Allows the passing of additional style classes to the component root node - */ - classNames: PropTypes.string, -}; - -function useApplicationTileHeaderStyles(dense) { - return { - root: { - backgroundColor: "primary.main", - color: "common.white", - paddingTop: dense ? 0.5 : 1, - paddingBottom: dense ? 0.5 : 1, - paddingRight: 1, - paddingLeft: 1, - display: "flex", - justifyContent: "space-between", - alignItems: "center", - }, - subHeaderContainers: { - paddingTop: 0, - paddingBottom: 0, - paddingRight: 1, - paddingLeft: 1, - }, - leftHeaderContent: { - marginRight: "auto", - }, - rightHeaderContent: { - marginLeft: "auto", - }, - }; -} diff --git a/components/airview-compliance-ui/features/application-tile/application-tile-title.js b/components/airview-compliance-ui/features/application-tile/application-tile-title.js deleted file mode 100644 index 960dc839..00000000 --- a/components/airview-compliance-ui/features/application-tile/application-tile-title.js +++ /dev/null @@ -1,54 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { Typography } from "@mui/material"; - -export function ApplicationTileTitle({ - children, - level = "h2", - color = "inherit", - classNames, -}) { - const classes = useApplicationTileStyles(color); - - return ( - - {children} - - ); -} - -ApplicationTileTitle.propTypes = { - /** - * The content of the component (the title string) - */ - children: PropTypes.string, - /** - * Sets the semantic HTML level of the title [heading element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Heading_Elements) - */ - level: PropTypes.oneOf(["h1", "h2", "h3", "h4", "h5", "h6"]), - /** - * The color for the progress bar, should be a valid [CSS color value](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value) - */ - color: PropTypes.string, - /** - * Allows the passing of additional style classes to the component root node - */ - classNames: PropTypes.string, -}; - -function useApplicationTileStyles(color) { - return { - root: { - fontSize: "14px", - lineHeight: 1.3, - color: color, - }, - }; -} diff --git a/components/airview-compliance-ui/features/application-tile/application-tile.js b/components/airview-compliance-ui/features/application-tile/application-tile.js deleted file mode 100644 index 928fc2ea..00000000 --- a/components/airview-compliance-ui/features/application-tile/application-tile.js +++ /dev/null @@ -1,39 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { Box } from "@mui/material"; - -export function ApplicationTile({ children, gutter = false, classNames }) { - const classes = useApplicationTileStyles(gutter); - return ( - - {children} - - ); -} - -ApplicationTile.propTypes = { - /** - * Supported sub-components to create the ApplicationTile composition - */ - children: PropTypes.node, - /** - * Adds bottom margin to the root node - */ - gutter: PropTypes.bool, - /** - * Allows the passing of additional style classes to the component root node - */ - classNames: PropTypes.string, -}; - -function useApplicationTileStyles(gutter) { - return { - root: { - backgroundColor: "common.white", - border: 1, - borderColoe: "primary.main", - borderRadius: 1, - marginBottom: gutter ? 2 : 0, - }, - }; -} diff --git a/components/airview-compliance-ui/features/application-tile/index.js b/components/airview-compliance-ui/features/application-tile/index.js deleted file mode 100644 index 4b8e534d..00000000 --- a/components/airview-compliance-ui/features/application-tile/index.js +++ /dev/null @@ -1,8 +0,0 @@ -export { ApplicationTile } from "./application-tile"; -export { ApplicationTileTitle } from "./application-tile-title"; -export { ApplicationTileHeader } from "./application-tile-header"; -export { ApplicationTileDivider } from "./application-tile-divider"; -export { ApplicationTileContent } from "./application-tile-content"; -export { ApplicationTileContentRow } from "./application-tile-content-row"; -export { ApplicationTileCallToActionButton } from "./application-tile-call-to-action-button"; -export { ApplicationTileChip } from "./application-tile-chip"; diff --git a/components/airview-compliance-ui/features/compliance-table/compliance-table-accept-risk-dialog.js b/components/airview-compliance-ui/features/compliance-table/compliance-table-accept-risk-dialog.js deleted file mode 100644 index 94c2ebda..00000000 --- a/components/airview-compliance-ui/features/compliance-table/compliance-table-accept-risk-dialog.js +++ /dev/null @@ -1,533 +0,0 @@ -import React, { useState, useMemo } from "react"; -import PropTypes from "prop-types"; -import { useTheme } from "@mui/material/styles"; -import { - Button, - Dialog, - DialogTitle, - DialogActions, - DialogContent, - Grid, - TextField, - Switch, - Typography, - Box, - MenuItem, - FormLabel, - FormControl, - Select, - ButtonGroup, - CircularProgress, - InputLabel, -} from "@mui/material"; -import WarningIcon from "@mui/icons-material/Warning"; - -import { LocalizationProvider, DatePicker } from "@mui/x-date-pickers"; -import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; -import { IconChip } from "../icon-chip/icon-chip"; - -import { complianceTableAcceptRiskDialogStyles } from "./compliance-table-accept-risk-dialog.style"; - -export function ComplianceTableAcceptRiskDialog({ - open, - onClose, - onAccept, - exemptions, - applicationId, - controlId, - impactLevel, -}) { - const theme = useTheme(); - const classes = complianceTableAcceptRiskDialogStyles(); - const probabilities = useMemo( - () => ["Low", "Moderate", "High", "Absolute"], - [] - ); - const impacts = useMemo(() => ["Low", "Medium", "High"], []); - const impactLevelData = { - 1: { - label: "Low", - color: theme.palette.success.main, - }, - 2: { - label: "Medium", - color: theme.palette.warning.main, - }, - 3: { - label: "High", - color: theme.palette.error.main, - }, - }; - const initialState = { - summary: { - value: "", - valid: false, - }, - mitigation: { - value: "", - valid: false, - }, - probability: { - value: probabilities[0], - valid: true, - }, - impact: { - value: impacts[0], - valid: true, - }, - resources: { - value: [], - valid: false, - }, - limitedExemption: { - value: false, - }, - exemptionEnd: { - value: new Date(), - }, - notes: { - value: "", - }, - }; - const [formData, setFormData] = useState({ - ...initialState, - }); - const [submitting, setSubmitting] = useState(false); - - const isFormValid = () => { - let formValid = true; - - for (const [, value] of Object.entries(formData)) { - if (value.valid !== undefined && value.valid === false) { - formValid = false; - break; - } - } - - return formValid; - }; - - const getImpactLevel = useMemo( - () => - impactLevel( - impacts.indexOf(formData.impact.value) + 1, - probabilities.indexOf(formData.probability.value) + 1 - ), - [ - formData.probability.value, - formData.impact.value, - probabilities, - impacts, - impactLevel, - ] - ); - - const handleOnSubmit = async () => { - try { - setSubmitting(true); - - await onAccept({ - ...formData, - applicationId: { value: applicationId }, - controlId: { value: controlId }, - }); - - onClose(); - } catch { - // // console.log("Something went wrong attempting to submit risk"); - } - }; - - const handleOnExit = () => { - setFormData({ ...initialState }); - setSubmitting(false); - }; - - return ( - { - if (reason !== "backdropClick") onClose(); - }} - maxWidth="md" - fullWidth - TransitionProps={{ - onExited: handleOnExit, - }} - > - Accept Risks - - - - - { - const value = event.target.value; - setFormData({ - ...formData, - summary: { - value: value.trimStart(), - valid: value.trimStart().length > 0 ? true : false, - }, - }); - }} - disabled={submitting} - /> - - - - { - const value = event.target.value; - setFormData({ - ...formData, - mitigation: { - value: value.trimStart(), - valid: value.trimStart().length > 0 ? true : false, - }, - }); - }} - disabled={submitting} - /> - - - - - Probability - - - - {probabilities.map((probability) => ( - - ))} - - - - - - Impact - - - - {impacts.map((impact) => ( - - ))} - - - - - Impact Level - } - label={impactLevelData[getImpactLevel].label} - labelColor="#fff" - aria-label="Impact Level" - /> - - - - - - Resources - - - - - - - - Limited Exemption? - - { - const value = event.target.checked; - setFormData({ - ...formData, - limitedExemption: { - value, - }, - exemptionEnd: { - ...formData.exemptionEnd, - value: value ? formData.exemptionEnd.value : new Date(), - valid: true, - }, - }); - }} - name="Limited exemption" - color="primary" - size="small" - edge="start" - id={`limited-exemption-${applicationId}`} - disabled={submitting} - /> - - - - {formData.limitedExemption.value && ( - - { - let valid = true; - - if (!date || !date.isValid() || date.isBefore(new Date())) { - valid = false; - } - - setFormData({ - ...formData, - exemptionEnd: { - value: date, - valid: valid, - }, - }); - }} - renderInput={(params) => ( - - )} - disabled={submitting} - PopperProps={{ placement: "auto" }} - /> - - )} - - - - { - const value = event.target.value; - setFormData({ - ...formData, - notes: { - value: value.trimStart(), - }, - }); - }} - disabled={submitting} - /> - - - - - * denotes a required field - - - - - - - - - {submitting && ( - - - - )} - - - - - - ); -} - -ComplianceTableAcceptRiskDialog.propTypes = { - /** - * If `true` reveals the dialog, if `false` hides the dialog - */ - open: PropTypes.bool.isRequired, - /** - * Callback for when the dialog is requesting to close - */ - onClose: PropTypes.func.isRequired, - /** - * Callback for when a user accepts the dialog, excepts a promise resolution as a result of the callback. **Signature:** `function(formData: object) => Promise` - */ - onAccept: PropTypes.func.isRequired, - /** - * Array of application exemptions to process - */ - exemptions: PropTypes.arrayOf( - PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired, - status: PropTypes.oneOf(["none", "pending", "active"]), - }) - ).isRequired, - /** - * Unique application ID - */ - applicationId: PropTypes.number.isRequired, - /** - * Callback to calculate the impact level, should return a value from a given impact and probability. **Signature:** `function(impact: int, probability: int) => enum[1, 2, 3] : int` - */ - impactLevel: PropTypes.func.isRequired, -}; diff --git a/components/airview-compliance-ui/features/compliance-table/compliance-table-accept-risk-dialog.style.js b/components/airview-compliance-ui/features/compliance-table/compliance-table-accept-risk-dialog.style.js deleted file mode 100644 index b95e9d4c..00000000 --- a/components/airview-compliance-ui/features/compliance-table/compliance-table-accept-risk-dialog.style.js +++ /dev/null @@ -1,60 +0,0 @@ -export function complianceTableAcceptRiskDialogStyles() { - return { - border: { - border: "1px solid #000", - }, - limitedExemptionGridItem: { - display: "flex", - flexDirection: "column", - }, - limitedExemptionLabel: { - margin: 0, - }, - limitedexemptionswitch: { - marginTop: "-2px", - }, - exemptionEnd: { - margin: 0, - width: "100%", - - "& .MuiInputBase-input": { - paddingTop: "10.5px", - paddingBottom: "10.5px", - height: "1.1876em", - }, - - "& .MuiInputLabel-outlined:not(.MuiInputLabel-shrink)": { - transform: "translate(14px, 12px) scale(1)", - }, - }, - inputLabel: { - display: "block", - marginTop: "-5px", - marginBottom: "6px", - fontSize: 12, - }, - selectedBtnGroupItem: { - "&, &:hover": { - backgroundColor: "primary.main", - color: "common.white", - }, - "&[disabled]": { - backgroundColor: "action.disabledBackground", - color: "action.disabled", - }, - }, - submitActionContainer: { - position: "relative", - }, - submitActionProgressContainer: { - position: "absolute", - display: "inline-flex", - top: 0, - left: 0, - width: "100%", - height: "100%", - justifyContent: "center", - alignItems: "center", - }, - }; -} diff --git a/components/airview-compliance-ui/features/compliance-table/compliance-table-head.js b/components/airview-compliance-ui/features/compliance-table/compliance-table-head.js deleted file mode 100644 index 37455046..00000000 --- a/components/airview-compliance-ui/features/compliance-table/compliance-table-head.js +++ /dev/null @@ -1,71 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { - TableHead, - TableRow, - TableCell, - TableSortLabel, - Box, -} from "@mui/material"; -import { complianceTableCommonStyles } from "./compliance-table.common-styles"; -import { complianceTableHeadStyles } from "./compliance-table-head.styles"; - -function ComplianceTableHead({ ageOrder, sortable, onSortClick }) { - const classes = complianceTableHeadStyles(); - const sharedClasses = complianceTableCommonStyles(); - - return ( - - - - - - Name - - - - Ticket/s - - - - {sortable ? ( - - Age - - {ageOrder === "desc" - ? "Age sorted descending" - : "Age sorted ascending"} - - - ) : ( - "Age" - )} - - - - - - ); -} - -ComplianceTableHead.propTypes = { - ageOrder: PropTypes.oneOf(["asc", "desc"]).isRequired, - sortable: PropTypes.bool.isRequired, - onSortClick: PropTypes.func.isRequired, -}; - -export { ComplianceTableHead }; diff --git a/components/airview-compliance-ui/features/compliance-table/compliance-table-head.styles.js b/components/airview-compliance-ui/features/compliance-table/compliance-table-head.styles.js deleted file mode 100644 index 0d6f9638..00000000 --- a/components/airview-compliance-ui/features/compliance-table/compliance-table-head.styles.js +++ /dev/null @@ -1,13 +0,0 @@ -export function complianceTableHeadStyles() { - return { - // Tickets table column - ticketsTableColumn: { - width: 250, - }, - - // Age table column - ageTableColumn: { - width: 180, - }, - }; -} diff --git a/components/airview-compliance-ui/features/compliance-table/compliance-table-row-detail.js b/components/airview-compliance-ui/features/compliance-table/compliance-table-row-detail.js deleted file mode 100644 index f682dbca..00000000 --- a/components/airview-compliance-ui/features/compliance-table/compliance-table-row-detail.js +++ /dev/null @@ -1,143 +0,0 @@ -import React, { useState, useMemo } from "react"; -import PropTypes from "prop-types"; -import { Box, Button } from "@mui/material"; -import OpenInNewIcon from "@mui/icons-material/OpenInNew"; -import { getRisk } from "./get-risk"; -import { complianceTableRowDetailStyles } from "./compliance-table-row-detail.styles"; -import { ComplianceTableAcceptRiskDialog } from "./compliance-table-accept-risk-dialog"; - -function ComplianceTableRowDetail({ detailData, onAcceptOfRisk }) { - const classes = complianceTableRowDetailStyles(); - const [dialogOpen, setDialogOpen] = useState(false); - const handleOnClose = () => setDialogOpen(false); - - const actionableRisks = useMemo(() => { - return detailData.resources.filter((instance) => instance.status === "none") - .length; - }, [detailData]); - - return ( - - - - - Resources: - - {detailData.resources.map((instance) => ( - - {instance.name} - {instance.status === "pending" && " (pending)"} - - ))} - - - - - Control: - - {detailData.controlName} - - - - - - Frameworks: - - {detailData.frameworks?.map((framework, index) => { - return ( - - ); - })} - - - - - Assignment Group: - {detailData.assignmentGroup} - - - - Assignee: - {detailData.assignee} - - - - System Source: - {detailData.systemName} - - - - System Stage: - {detailData.systemStage} - - - - - - {/* - - */} - - - - - ); -} - -ComplianceTableRowDetail.propTypes = { - detailData: PropTypes.object.isRequired, - onAcceptOfRisk: PropTypes.func.isRequired, - applicationId: PropTypes.number.isRequired, -}; - -export { ComplianceTableRowDetail }; diff --git a/components/airview-compliance-ui/features/compliance-table/compliance-table-row-detail.styles.js b/components/airview-compliance-ui/features/compliance-table/compliance-table-row-detail.styles.js deleted file mode 100644 index 3e93b823..00000000 --- a/components/airview-compliance-ui/features/compliance-table/compliance-table-row-detail.styles.js +++ /dev/null @@ -1,97 +0,0 @@ -export function complianceTableRowDetailStyles() { - return { - // Instance row additional information container - additionalInfoContainer: { - borderTop: 1, - borderTopColor: "divider", - display: "flex", - justifyContent: "space-between", - }, - - instanceInfo: { - listStyle: "none", - padding: 2, - margin: 0, - flex: "1 1 74.5%", - }, - - instanceInfoItem: { - padding: 1, - display: "flex", - justifyContent: "space-between", - alignItems: "center", - - "&:not(:last-of-type)": { - borderBottom: 1, - borderBottomColor: "divider", - }, - - "& > span:first-of-type": { - fontWeight: "bold", - }, - - "& .MuiButton-root": { - marginLeft: 1, - fontSize: 12, - - "& .MuiSvgIcon-root": { - fontSize: 14, - }, - }, - }, - - instances: { - listStyle: "none", - textAlign: "right", - display: "flex", - justifyContent: "flex-end", - flexWrap: "wrap", - }, - - instanceItem: { - paddingLeft: 1, - wordBreak: "break-word", - - "&:not(:last-child):after": { - content: "','", - }, - }, - - pendingInstance: { - color: "text.primary", - }, - - control: { - textAlign: "right", - wordBreak: "break-word", - }, - - // // Instance actions - instanceActions: { - flex: "1 1 25.5%", - paddingTop: 3, - paddingRight: 2, - - "& .MuiButton-root": { - marginBottom: 1, - fontSize: 12, - }, - }, - - // Loading skeleton - loadingInstanceInfoRow: { - margin: 1, - }, - - instanceActionsLoading: { - paddingTop: 2, - }, - - // Failed data loding feedback message - loadingErrorFeedbackContainer: { - borderTop: 1, - borderTopColor: "divider", - padding: 2, - }, - }; -} diff --git a/components/airview-compliance-ui/features/compliance-table/compliance-table-row.js b/components/airview-compliance-ui/features/compliance-table/compliance-table-row.js deleted file mode 100644 index ed5450d4..00000000 --- a/components/airview-compliance-ui/features/compliance-table/compliance-table-row.js +++ /dev/null @@ -1,149 +0,0 @@ -import React, { useState, useMemo } from "react"; -import PropTypes from "prop-types"; -import { - TableRow, - TableCell, - Tooltip, - Chip, - Collapse, - Box, - IconButton, -} from "@mui/material"; -import SecurityIcon from "@mui/icons-material/Security"; -import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; -import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp"; -import { complianceTableCommonStyles } from "./compliance-table.common-styles"; -import { complianceTableRowStyles } from "./compliance-table-row.styles"; - -function ComplianceTableRow({ - environmentName, - raisedDate, - timeSinceRaised, - qualityModel, - severity, - technicalControlName, - tickets, - children, -}) { - const classes = complianceTableRowStyles(); - const sharedClasses = complianceTableCommonStyles(); - const [open, setOpen] = useState(false); - - const IconComponent = useMemo(() => { - return SecurityIcon; - - /* - To do: it doesn't make sense to use this data as the drill down is based off of qualityModel, so these will always be the same per group - if (qualityModel === "security") return SecurityIcon; - if (qualityModel === "operational") return PowerSettingsNewIcon; - if (qualityModel === "task") return AssignmentTurnedInIcon; - */ - }, []); - - return ( - - - - - - - - - {`Control type: ${qualityModel}. Severity: ${severity}`} - - - - - - {technicalControlName} - - - {environmentName} - - - - - - {tickets?.map((ticket, index) => ( - - ))} - - - - - - {timeSinceRaised} - - - ({raisedDate}) - - - - - setOpen(!open)} - > - {open ? : } - - - - - - - - {children} - - - - - ); -} - -ComplianceTableRow.propTypes = { - environment: PropTypes.string.isRequired, - raisedDate: PropTypes.string.isRequired, - timeSinceRaised: PropTypes.string.isRequired, - qualityModel: PropTypes.oneOf(["security", "operational", "task"]).isRequired, - severity: PropTypes.oneOf(["high", "medium", "low"]).isRequired, - name: PropTypes.string.isRequired, - tickets: PropTypes.arrayOf( - PropTypes.shape({ - reference: PropTypes.string.isRequired, - type: PropTypes.oneOf(["incident", "problem", "risk"]), - }) - ).isRequired, - children: PropTypes.node, -}; - -export { ComplianceTableRow }; diff --git a/components/airview-compliance-ui/features/compliance-table/compliance-table-row.styles.js b/components/airview-compliance-ui/features/compliance-table/compliance-table-row.styles.js deleted file mode 100644 index f74ac36c..00000000 --- a/components/airview-compliance-ui/features/compliance-table/compliance-table-row.styles.js +++ /dev/null @@ -1,95 +0,0 @@ -export function complianceTableRowStyles() { - return { - // Table body rows - tableBodyRowRoot: { - "& > *": { - borderBottom: "unset", - }, - }, - - // Control status Icon styles - controlStatusIconRoot: { - width: 26, - height: 26, - padding: "4px", - borderRadius: "100%", - color: "#fff", - display: "block", - }, - - controlStatusIconFontSmall: { - fontSize: "16px", - }, - - controlStatusIconSeverity_high: { - backgroundColor: "error.main", - }, - - controlStatusIconSeverity_medium: { - backgroundColor: "warning.light", - }, - - controlStatusIconSeverity_low: { - backgroundColor: "grey.500", - }, - - // Name / Environment information - nameEnvBase: { - display: "block", - wordBreak: "break-word", - }, - - env: { - color: "grey.600", - fontSize: 12, - }, - - // Application tickets - applicationTicketsContainer: { - display: "flex", - flexWrap: "wrap", - margin: "-4px", - }, - - applicationTicketLabel: { - fontWeight: "medium", - }, - - applicationTicketRoot: { - margin: 0.5, - borderRadius: 1, - flex: "1 1 auto", - maxWidth: 250, - }, - - applicationTicketType_incident: { - backgroundColor: "error.main", - color: "error.contrastText", - }, - - applicationTicketType_problem: { - backgroundColor: "warning.light", - color: "rgba(0, 0, 0, 0.87)", - }, - - applicationTicketType_risk: { - backgroundColor: "grey.400", - color: "rgba(0, 0, 0, 0.87)", - }, - - // Age information - ageInfoBase: { - display: "block", - }, - - age: { - composes: "$ageInfoBase", - }, - - raisedDate: { - composes: "$ageInfoBase", - color: "grey.600", - fontSize: 12, - }, - }; -} diff --git a/components/airview-compliance-ui/features/compliance-table/compliance-table-toolbar.js b/components/airview-compliance-ui/features/compliance-table/compliance-table-toolbar.js deleted file mode 100644 index 9502b849..00000000 --- a/components/airview-compliance-ui/features/compliance-table/compliance-table-toolbar.js +++ /dev/null @@ -1,128 +0,0 @@ -import React, { useState } from "react"; -import PropTypes from "prop-types"; -import { - Toolbar, - Typography, - Tooltip, - Menu, - MenuItem, - ListItemText, - Checkbox, - Box, - IconButton, - ListItemIcon, -} from "@mui/material"; -import FilterListIcon from "@mui/icons-material/FilterList"; -import { complianceTableToolbarStyles } from "./compliance-table-toolbar.styles"; - -function ComplianceTableToolbar({ - title, - filters, - activeFilters, - onFilterChange, - testid, -}) { - const classes = complianceTableToolbarStyles(); - - const [anchorEl, setAnchorEl] = useState(null); - - const handleClick = (event) => { - setAnchorEl(event.currentTarget); - }; - - const handleClose = () => { - setAnchorEl(null); - }; - - if (!title && (!filters || filters.length < 2)) return null; - - return ( - - {title && ( - - {title} - - )} - - {filters && filters.length > 1 && ( - - - - - - - - - - - {filters.map((filter) => ( - onFilterChange(filter)} - dense - key={filter} - > - - - - - - - ))} - - - )} - - ); -} - -ComplianceTableToolbar.propTypes = { - title: PropTypes.string, - filters: PropTypes.arrayOf(PropTypes.string), - activeFilters: PropTypes.arrayOf(PropTypes.string), - onFilterChange: PropTypes.func, - testid: PropTypes.string, -}; - -export { ComplianceTableToolbar }; diff --git a/components/airview-compliance-ui/features/compliance-table/compliance-table-toolbar.styles.js b/components/airview-compliance-ui/features/compliance-table/compliance-table-toolbar.styles.js deleted file mode 100644 index 9b02511c..00000000 --- a/components/airview-compliance-ui/features/compliance-table/compliance-table-toolbar.styles.js +++ /dev/null @@ -1,26 +0,0 @@ -export function complianceTableToolbarStyles() { - return { - toolbar: { - paddingLeft: 2, - paddingRight: 1, - }, - - toolbarFilters: { - marginLeft: "auto", - }, - - checkboxContainer: { - minWidth: 30, - }, - - checkbox: { - padding: 0, - - "$checkboxChecked&:hover, &:hover": { - backgroundColor: "transparent", - }, - }, - - checkboxChecked: {}, - }; -} diff --git a/components/airview-compliance-ui/features/compliance-table/compliance-table.common-styles.js b/components/airview-compliance-ui/features/compliance-table/compliance-table.common-styles.js deleted file mode 100644 index 91f2041f..00000000 --- a/components/airview-compliance-ui/features/compliance-table/compliance-table.common-styles.js +++ /dev/null @@ -1,21 +0,0 @@ -export function complianceTableCommonStyles() { - return { - // Application name table cell styles - applicationTableCell: { - paddingLeft: 0, - }, - - // Hide elements for screen readers only - visuallyHidden: { - border: 0, - clip: "rect(0 0 0 0)", - height: "1px", - margin: -1, - overflow: "hidden", - padding: 0, - position: "absolute", - top: 20, - width: "1px", - }, - }; -} diff --git a/components/airview-compliance-ui/features/compliance-table/compliance-table.js b/components/airview-compliance-ui/features/compliance-table/compliance-table.js deleted file mode 100644 index 6b8e3401..00000000 --- a/components/airview-compliance-ui/features/compliance-table/compliance-table.js +++ /dev/null @@ -1,269 +0,0 @@ -import React, { useState, useMemo } from "react"; -import PropTypes from "prop-types"; -import { useTheme } from "@mui/material/styles"; -import { - Paper, - Table, - TableContainer, - TableBody, - Skeleton, -} from "@mui/material"; -import dayjs from "dayjs"; -import relativeTime from "dayjs/plugin/relativeTime"; -import updateLocale from "dayjs/plugin/updateLocale"; -import { ComplianceTableToolbar } from "./compliance-table-toolbar"; -import { ComplianceTableHead } from "./compliance-table-head"; -import { ComplianceTableRow } from "./compliance-table-row"; -import { ComplianceTableRowDetail } from "./compliance-table-row-detail"; -import { Message } from "../../components"; - -function ComplianceTable({ - title, - applications, - onAcceptOfRisk, - loading, - noDataMessage, - invalidPermissionsMessage, -}) { - const theme = useTheme(); - - const [ageOrder, setAgeOrder] = useState("desc"); - const [activeFilters, setActiveFilters] = useState([]); - - const getFiltersDerrivedFromApplicationData = (applications) => { - if (!applications) return; - - const derrivedFilters = applications - .map((application) => application.environmentName) - .reduce((uniqueFilters, filter) => { - return uniqueFilters.includes(filter) - ? uniqueFilters - : [...uniqueFilters, filter]; - }, []) - .sort(); - - return derrivedFilters; - }; - - const getApplicationsSortedByAge = (applications, sortBy) => { - return [ - ...applications.sort((a, b) => { - if (sortBy === "asc") { - return Date.parse(b.raisedDateTime) - Date.parse(a.raisedDateTime); - } - if (sortBy === "desc") { - return Date.parse(a.raisedDateTime) - Date.parse(b.raisedDateTime); - } - - return 0; - }), - ]; - }; - - const getApplicationsByFilterValues = (applications, filterValues) => { - if (filterValues.length < 1) return applications; - - return applications.filter((application) => - filterValues.includes(application.environmentName) - ); - }; - - const handleOnSortByAgeClick = () => { - if (ageOrder === "asc") { - setAgeOrder("desc"); - } else { - setAgeOrder("asc"); - } - }; - - const handleOnFilterChange = (filterId) => { - if (activeFilters.includes(filterId)) { - setActiveFilters(activeFilters.filter((id) => id !== filterId)); - } else { - setActiveFilters([...activeFilters, filterId]); - } - }; - - const getTicketTimeData = (raisedDateTime) => { - dayjs.extend(relativeTime); - dayjs.extend(updateLocale); - dayjs.updateLocale("en", { - relativeTime: { - future: "in %s", - past: "%s ago", - s: "a few seconds", - m: "1 minute", - mm: "%d minutes", - h: "1 hour", - hh: "%d hours", - d: "1 day", - dd: "%d days", - M: "1 month", - MM: "%d months", - y: "1 year", - yy: "%d years", - }, - }); - - const raisedDate = dayjs(raisedDateTime); - const timeSinceRaised = raisedDate.from(dayjs(), true); - - return { - raisedDate: raisedDate.format("MMM D YYYY - HH:mm"), - timeSinceRaised, - }; - }; - - const getProcessedApplicationData = useMemo(() => { - if (loading || !applications) return; - - return getApplicationsByFilterValues( - getApplicationsSortedByAge(applications, ageOrder), - activeFilters - ); - }, [applications, activeFilters, ageOrder, loading]); - - if (loading) { - return ; - } - - if (!applications) { - return ( - - ); - } - - if (applications.length < 1) { - return ( - - ); - } - - const filters = getFiltersDerrivedFromApplicationData(applications); - - return ( - - - - 1} - ageOrder={ageOrder} - /> - - - {getProcessedApplicationData.map((application) => ( - - - - ))} - -
-
- ); -} - -ComplianceTable.propTypes = { - /** - * Presents the component in a lodaing state (for when fetching data async) - */ - loading: PropTypes.bool, - /** - * An optional title for the table - */ - title: PropTypes.string, - /** - * The collection of applications to render to the complianceTable, should be an array of applications, an empty array (for no issues) of null (for invalid permissions) - */ - applications: PropTypes.arrayOf( - PropTypes.shape({ - id: PropTypes.number.isRequired, - qualityModel: PropTypes.oneOf(["security", "operational", "task"]) - .isRequired, - severity: PropTypes.oneOf(["high", "medium", "low"]).isRequired, - name: PropTypes.string.isRequired, - tickets: PropTypes.arrayOf( - PropTypes.shape({ - reference: PropTypes.string.isRequired, - type: PropTypes.oneOf(["incident", "problem", "risk"]), - }) - ).isRequired, - raisedDateTime: (props, propName, componentName) => { - if (isNaN(Date.parse(props[propName]))) { - return new Error( - "Invalid prop `" + - propName + - "` supplied to" + - " `" + - componentName + - "`. Should be valid Date Time string." - ); - } - }, - environment: PropTypes.string.isRequired, - applicationDetailData: PropTypes.shape({ - instances: PropTypes.arrayOf( - PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired, - status: PropTypes.oneOf(["none", "pending"]).isRequired, - }) - ), - control: PropTypes.shape({ - name: PropTypes.string.isRequired, - url: PropTypes.string.isRequired, - }), - frameworks: PropTypes.arrayOf( - PropTypes.shape({ - name: PropTypes.string.isRequired, - url: PropTypes.string.isRequired, - }) - ), - environment: PropTypes.string, - assignmentGroup: PropTypes.string, - assignee: PropTypes.string, - systemSource: PropTypes.string, - systemStage: PropTypes.string, - }), - }) - ), - /** - * Callback for when a user accepts the risk dialog, excepts a promise resolution as a result of the callback. **Signature:** `function(formData: object) => Promise` - */ - onAcceptOfRisk: PropTypes.func, - /** - * Used to display a message to the user when they do not have required permissions to view the application data - */ - invalidPermissionsMessage: PropTypes.shape({ - title: PropTypes.string.isRequired, - message: PropTypes.string.isRequired, - }).isRequired, - /** - * Used to display a message to the user when there is no data to display for the application - */ - noDataMessage: PropTypes.shape({ - title: PropTypes.string.isRequired, - message: PropTypes.string.isRequired, - }).isRequired, -}; - -export { ComplianceTable }; diff --git a/components/airview-compliance-ui/features/compliance-table/get-risk.js b/components/airview-compliance-ui/features/compliance-table/get-risk.js deleted file mode 100644 index 52bfb4a9..00000000 --- a/components/airview-compliance-ui/features/compliance-table/get-risk.js +++ /dev/null @@ -1,7 +0,0 @@ -export function getRisk(impact, probability) { - if (impact === 2 && probability === 3) return 3; - const n = impact + probability; - if (n < 4) return 1; - if (n > 5) return 3; - return 2; -} diff --git a/components/airview-compliance-ui/features/compliance-table/index.js b/components/airview-compliance-ui/features/compliance-table/index.js deleted file mode 100644 index add09c48..00000000 --- a/components/airview-compliance-ui/features/compliance-table/index.js +++ /dev/null @@ -1 +0,0 @@ -export { ComplianceTable } from "./compliance-table"; diff --git a/components/airview-compliance-ui/features/control-overview/control-overview-group.js b/components/airview-compliance-ui/features/control-overview/control-overview-group.js deleted file mode 100644 index 025038c8..00000000 --- a/components/airview-compliance-ui/features/control-overview/control-overview-group.js +++ /dev/null @@ -1,75 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { - Accordion, - AccordionSummary, - AccordionDetails, - Typography, -} from "@mui/material"; -import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; - -export function ControlOverviewGroup({ groupTitle, id, onChange, children }) { - const classes = controlOverviewGroupStyles(); - - const handleOnChange = (event, expanded) => { - if (expanded) onChange(id); - }; - - return ( - - } - sx={classes.overviewGroupSummary} - > - {groupTitle} - - - - {children} - - - ); -} - -ControlOverviewGroup.propTypes = { - groupTitle: PropTypes.string, - id: PropTypes.number, - onChange: PropTypes.func, - children: PropTypes.node, -}; - -function controlOverviewGroupStyles() { - return { - overviewGroup: { - boxShadow: "none", - "&:not(:last-child)": { - borderBottom: 0, - }, - "&:before": { - display: "none", - }, - "&.Mui-expanded": { - margin: "auto", - }, - "&:not(:first-of-type)": { - borderTop: "1px solid rgba(0, 0, 0, .125)", - }, - }, - overviewGroupExpanded: {}, - overviewGroupSummary: {}, - overviewGroupTitle: { - fontSize: 16, - fontWeight: "medium", - }, - overviewGroupChildren: { - display: "block", - padding: 0, - borderTop: "1px solid rgba(0, 0, 0, .125)", - }, - }; -} diff --git a/components/airview-compliance-ui/features/control-overview/control-overview-header.js b/components/airview-compliance-ui/features/control-overview/control-overview-header.js deleted file mode 100644 index 0ab2ecc9..00000000 --- a/components/airview-compliance-ui/features/control-overview/control-overview-header.js +++ /dev/null @@ -1,34 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { Toolbar, Typography } from "@mui/material"; - -export function ControlOverviewHeader({ title }) { - const classes = controlOverviewHeaderStyles(); - - return ( - - - {title} - - - ); -} - -ControlOverviewHeader.propTypes = { - title: PropTypes.string, -}; - -function controlOverviewHeaderStyles() { - return { - header: { - paddingTop: 1, - paddingBottom: 1, - paddingRight: 2, - paddingLeft: 2, - }, - }; -} diff --git a/components/airview-compliance-ui/features/control-overview/control-overview-item-detail.js b/components/airview-compliance-ui/features/control-overview/control-overview-item-detail.js deleted file mode 100644 index f2eef1d7..00000000 --- a/components/airview-compliance-ui/features/control-overview/control-overview-item-detail.js +++ /dev/null @@ -1,171 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { Grid, Button, Typography, Box } from "@mui/material"; -import OpenInNewIcon from "@mui/icons-material/OpenInNew"; - -export function ControlOverviewItemDetail({ - control, - frameworks, - controlAction, - lifecycle, -}) { - const classes = controlOverviewItemDetailStyles(); - - return ( - - - - - - Control: - - - - {control.name} - - - - - - - - Frameworks: - - - - {frameworks.map((framework) => ( - - ))} - - - - - - - Control Action: - - - - - {controlAction} - - - - - - Lifecycle: - - - - {lifecycle} - - - - - {/* - - - - - */} - - ); -} - -ControlOverviewItemDetail.propTypes = { - control: PropTypes.shape({ - name: PropTypes.string, - url: PropTypes.string, - }), - frameworks: PropTypes.arrayOf( - PropTypes.shape({ - name: PropTypes.string, - url: PropTypes.string, - }), - ), - controlAction: PropTypes.string, - lifecycle: PropTypes.string, -}; - -function controlOverviewItemDetailStyles() { - return { - itemDetail: { - display: "flex", - justifyContent: "space-between", - alignItems: "center", - paddingTop: 1, - paddingBottom: 1, - paddingRight: 0, - paddingLeft: 0, - - "&:first-of-type": { - paddingTop: 0, - }, - - "&:not(:last-of-type)": { - borderBottom: 1, - borderBottomColor: "divider", - }, - }, - - itemDetailLeftContent: { - flex: "0 0 auto", - paddingRight: 1, - }, - - itemDetailRightContent: { - flex: "1 1 auto", - display: "flex", - alignItems: "center", - justifyContent: "flex-end", - flexWrap: "wrap", - paddingLeft: 1, - - "& > *:not(:last-child)": { - marginRight: 1, - }, - }, - - itemDetailTitle: { - fontSize: 14, - fontWeight: "bold", - }, - - itemDetailAction: { - fontSize: 12, - - "& .MuiSvgIcon-root": { - fontSize: 14, - }, - }, - }; -} diff --git a/components/airview-compliance-ui/features/control-overview/control-overview-item-resources.js b/components/airview-compliance-ui/features/control-overview/control-overview-item-resources.js deleted file mode 100644 index eab31842..00000000 --- a/components/airview-compliance-ui/features/control-overview/control-overview-item-resources.js +++ /dev/null @@ -1,410 +0,0 @@ -import React, { useMemo, useState } from "react"; -import PropTypes from "prop-types"; - -import { - Table, - TableBody, - TableCell, - TableContainer, - TableHead, - TableRow, - TableSortLabel, - Paper, - Toolbar, - Typography, - Tooltip, - Menu, - MenuItem, - ListItemText, - Checkbox, - Box, -} from "@mui/material"; -import IconButton from "@mui/material/IconButton"; -import FilterListIcon from "@mui/icons-material/FilterList"; -import ListItemIcon from "@mui/material/ListItemIcon"; -import SettingsIcon from "@mui/icons-material/Settings"; -import CheckIcon from "@mui/icons-material/Check"; -import ClearIcon from "@mui/icons-material/Clear"; -import InfoIcon from "@mui/icons-material/Info"; - -import dayjs from "dayjs"; - -function capitalizeFirstLetter(string) { - return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase(); -} - -export function ControlOverviewItemResources({ - resourcesData, - onManageResourceClick, - onViewResourceEvidence, -}) { - const classes = controlOverviewItemResourcesStyles(); - - const [anchorEl, setAnchorEl] = useState(null); - const [activeFilters, setActiveFilters] = useState([]); - const [lastSeenOrder, setLastSeenOrder] = useState("desc"); - - const handleOnFilterClick = (event) => { - setAnchorEl(event.currentTarget); - }; - - const handleOnFilterClose = () => { - setAnchorEl(null); - }; - - const handleOnFilterChange = (selectedFilter) => { - if (activeFilters.includes(selectedFilter)) { - setActiveFilters( - activeFilters.filter((filter) => filter !== selectedFilter), - ); - } else { - setActiveFilters([...activeFilters, selectedFilter]); - } - }; - - const filters = useMemo(() => { - return resourcesData - .map((instance) => instance.environment) - .reduce((uniqueFilters, filter) => { - return uniqueFilters.includes(filter) - ? uniqueFilters - : [...uniqueFilters, filter]; - }, []) - .sort(); - }, [resourcesData]); - - const getResourcesByFilterValues = (resources, filterValues) => { - if (filterValues.length < 1) return resources; - - return resources.filter((instance) => - filterValues.includes(instance.environment), - ); - }; - - const getResourcesSortedByLastSeenDate = (resources, sortBy) => { - return [ - ...resources.sort((a, b) => { - if (sortBy === "asc") { - return Date.parse(a.lastSeen) - Date.parse(b.lastSeen); - } - if (sortBy === "desc") { - return Date.parse(b.lastSeen) - Date.parse(a.lastSeen); - } - - return 0; - }), - ]; - }; - - const processedResourcesData = useMemo(() => { - return getResourcesByFilterValues( - getResourcesSortedByLastSeenDate(resourcesData, lastSeenOrder), - activeFilters, - ); - }, [resourcesData, lastSeenOrder, activeFilters]); - - const handleOnSortByLastSeenClick = () => { - if (lastSeenOrder === "asc") { - setLastSeenOrder("desc"); - } else { - setLastSeenOrder("asc"); - } - }; - - const getStatusLabelClassName = (status) => { - let className; - - switch (status) { - case "Monitoring": - className = "statusLabelMonitoring"; - break; - case "Non-Compliant": - className = "statusLabelNonCompliant"; - break; - case "Exempt": - className = "statusLabelExempt"; - break; - default: - className = ""; - } - - return className; - }; - - return ( - - - - - Resources - - - {filters && filters.length > 1 && ( - - - - - - - - - - - {filters.map((filter) => { - return ( - handleOnFilterChange(filter)} - dense - key={filter} - > - - - - - - - ); - })} - - - )} - - - - - - Type - Resource - Environment - - {processedResourcesData.length > 1 ? ( - - Last seen - - {lastSeenOrder === "desc" - ? "Last seen sorted descending" - : "Last seen sorted ascending"} - - - ) : ( - "Last seen" - )} - - Status - Pending - - - - - - - {processedResourcesData.map((resource) => { - return ( - - {capitalizeFirstLetter(resource.type)} - {resource.name} - {resource.environment} - - {dayjs(resource.lastSeen).format("MMM D YYYY h:mm A")} - - - - {resource.status} - - - - {resource.pending ? ( - - ) : ( - - )} - - - {resource.status === "Exempt" && !resource.pending ? ( - - - onManageResourceClick(resource.id)} - > - - - - - ) : ( - "-" - )} - - - {resource?.evidence ? ( - - - onViewResourceEvidence(resource.id)} - > - - - - - ) : ( - "-" - )} - - - ); - })} - -
-
-
- ); -} - -ControlOverviewItemResources.propTypes = { - resourcesData: PropTypes.arrayOf( - PropTypes.shape({ - type: PropTypes.string, - reference: PropTypes.string, - environment: PropTypes.string, - lastSeen: PropTypes.string, - status: PropTypes.oneOf(["Monitoring", "Non-Compliant", "Exempt"]), - pending: PropTypes.bool, - }), - ), - onManageResourceClick: PropTypes.func.isRequired, - onViewResourceEvidence: PropTypes.func.isRequired, -}; - -function controlOverviewItemResourcesStyles() { - return { - container: { - marginTop: 2, - }, - toolbar: { - paddingLeft: 2, - paddingRight: 2, - backgroundColor: "grey.50", - }, - filterItem: { minWidth: 30 }, - filterCheckbox: { - padding: 0, - - "$filterCheckboxChecked&:hover, &:hover": { - backgroundColor: "transparent", - }, - }, - filterCheckboxChecked: {}, - visuallyHidden: { - border: 0, - clip: "rect(0 0 0 0)", - height: 1, - margin: -1, - overflow: "hidden", - padding: 0, - position: "absolute", - top: 20, - width: 1, - }, - tableHead: { - backgroundColor: "grey.50", - }, - tableBody: { - "& > tr:last-of-type > td": { - borderBottom: "none", - }, - }, - statusLabel: { - ..."typography.body2", - textTransform: "capitalize", - fontSize: 12, - fontWeight: "bolc", - border: `1px solid primary.main`, - borderRadius: 1, - padding: "2px 8px", - whiteSpace: "nowrap", - }, - - statusLabelMonitoring: { - backgroundColor: "success.main", - borderColor: "success.dark", - color: "common.white", - }, - - statusLabelNonCompliant: { - backgroundColor: "error.main", - borderColor: "error.dark", - color: "common.white", - }, - - statusLabelExempt: { - backgroundColor: "grey.300", - borderColor: "grey.400", - color: "grey.800", - }, - }; -} diff --git a/components/airview-compliance-ui/features/control-overview/control-overview-item.js b/components/airview-compliance-ui/features/control-overview/control-overview-item.js deleted file mode 100644 index e1e6f2ff..00000000 --- a/components/airview-compliance-ui/features/control-overview/control-overview-item.js +++ /dev/null @@ -1,169 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { - Accordion, - AccordionSummary, - AccordionDetails, - Typography, - Tooltip, - Box, -} from "@mui/material"; - -import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; -import ErrorIcon from "@mui/icons-material/Error"; - -export function ControlOverviewItem({ - id, - name, - severity, - applied, - exempt, - children, - onChange, -}) { - const classes = controlOverviewItemStyles(severity.toLowerCase()); - - const handleOnChange = (event, expanded) => { - if (expanded) onChange(id); - }; - - return ( - - } - > - - - - - - - {name} - - - - - - {applied} - - - - - - {exempt} - - - - - - - - {children} - - - ); -} - -ControlOverviewItem.propTypes = { - id: PropTypes.number, - name: PropTypes.string, - severity: PropTypes.oneOf(["Low", "Medium", "High"]), - applied: PropTypes.number, - exempt: PropTypes.number, - children: PropTypes.node, - onChange: PropTypes.func, -}; - -function controlOverviewItemStyles(severity) { - return { - overviewItem: { - width: "100%", - boxShadow: "none", - "&:not(:last-child)": { - borderBottom: 0, - }, - "&:before": { - display: "none", - }, - "&.Mui-expanded": { - margin: "auto", - }, - "&:not(:first-of-type)": { - borderTop: "1px solid rgba(0, 0, 0, .125)", - }, - }, - overviewItemExpanded: { margin: "auto" }, - overviewItemSummary: { - "$overviewItemExpanded &": { - borderBottom: "1px solid rgba(0, 0, 0, .125)", - }, - }, - overviewItemDetail: { - display: "block", - paddingTop: 2, - }, - severityStatus: () => { - const getBackgroundColor = (severity) => { - let backgroundColor; - - switch (severity) { - case "low": - backgroundColor = "success.main"; - break; - case "medium": - backgroundColor = "warning.main"; - break; - case "high": - backgroundColor = "error.main"; - break; - default: - backgroundColor = "grey.500"; - } - - return backgroundColor; - }; - - return { - width: 26, - height: 26, - padding: "4px", - borderRadius: "100%", - color: "#fff", - display: "inline-block", - backgroundColor: getBackgroundColor(severity), - fontSize: "16px", - marginRight: 2, - }; - }, - - infoLabel: { - display: "inline-block", - border: 1, - borderRadius: 1, - paddingTop: 0, - paddingBottom: 0, - paddingRight: 1, - paddingLeft: 1, - color: "text.primary", - - "&:not(:last-of-type)": { - marginRight: 1, - }, - }, - }; -} diff --git a/components/airview-compliance-ui/features/control-overview/control-overview-loading-indicator.js b/components/airview-compliance-ui/features/control-overview/control-overview-loading-indicator.js deleted file mode 100644 index 56b92cb0..00000000 --- a/components/airview-compliance-ui/features/control-overview/control-overview-loading-indicator.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { Box, CircularProgress } from "@mui/material"; - -export function ControlOverviewLoadingIndicator({ padding = false }) { - return ( - - - - ); -} - -ControlOverviewLoadingIndicator.propTypes = { - padding: PropTypes.bool, -}; diff --git a/components/airview-compliance-ui/features/control-overview/control-overview-resource-evidence-viewer.js b/components/airview-compliance-ui/features/control-overview/control-overview-resource-evidence-viewer.js deleted file mode 100644 index c19982c3..00000000 --- a/components/airview-compliance-ui/features/control-overview/control-overview-resource-evidence-viewer.js +++ /dev/null @@ -1,60 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { - Dialog, - DialogTitle, - DialogContent, - DialogActions, - Button, -} from "@mui/material"; -//import { MarkdownContent } from "../markdown-content"; - -export function ControlOverviewResourceEvidenceViewer({ - open, - onClose, - resourceEvidenceData, -}) { - return ( - - - Resource Supporting Evidence - - - - {/* */} - {resourceEvidenceData} - - - - - - - ); -} - -ControlOverviewResourceEvidenceViewer.propTypes = { - open: PropTypes.bool.isRequired, - onClose: PropTypes.func.isRequired, - resourceEvidenceData: PropTypes.string, -}; diff --git a/components/airview-compliance-ui/features/control-overview/control-overview-resource-manager.js b/components/airview-compliance-ui/features/control-overview/control-overview-resource-manager.js deleted file mode 100644 index 544885c1..00000000 --- a/components/airview-compliance-ui/features/control-overview/control-overview-resource-manager.js +++ /dev/null @@ -1,201 +0,0 @@ -import React, { useState, useEffect } from "react"; -import PropTypes from "prop-types"; -import { - Dialog, - DialogTitle, - DialogContent, - DialogActions, - Button, - Divider, - Typography, - Box, - TextField, -} from "@mui/material"; -import dayjs from "dayjs"; -import { LocalizationProvider, DatePicker } from "@mui/x-date-pickers"; -import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; -import { WorkingOverlay } from "./working-overlay"; - -export function ControlOverviewResourceManager({ - open, - onClose, - resourceData, - onResourceExemptionDelete, - onResourceExemptionSave, -}) { - const classes = controlOverviewResoueceManagerStyles(); - - const [revisedExpiryDate, setRevisedExpiryDate] = useState( - resourceData?.expires - ); - - const [working, setWorking] = useState(false); - - useEffect(() => { - setRevisedExpiryDate(resourceData?.expires); - }, [resourceData?.expires]); - - useEffect(() => { - if (!open) { - setWorking(false); - } - }, [open]); - - const handleOnResourceExemptionDelete = async () => { - setWorking(true); - - const { controlId, resourceId } = resourceData; - - await onResourceExemptionDelete({ controlId, resourceId }); - - onClose(); - }; - - const handleOnResourceExemptionSave = async () => { - setWorking(true); - - const { controlId, resourceId } = resourceData; - - await onResourceExemptionSave({ - controlId, - resourceId, - revisedExpiryDate: revisedExpiryDate.toISOString(), - }); - - onClose(); - }; - - if (!resourceData) return null; - - return ( - - - - - Manage Resource Exemption - - - - - Ticket: - - {resourceData.ticket} - - - - - Expires: - - - - To save this resource exemption, enter a new date greater than the - current expiry date. - - - - setRevisedExpiryDate(date)} - renderInput={(params) => ( - - )} - disabled={working} - PopperProps={{ placement: "auto" }} - /> - - - - - - Resources: - - - - {resourceData.resources.map((resource) => ( - - {resource} - - ))} - - - - - - - - - - ); -} - -function controlOverviewResoueceManagerStyles() { - return { - sectionHeader: { - fontSize: 15, - fontWeight: 600, - }, - sectionDivider: { - marginTop: 2, - marginBottom: 2, - }, - resourcesList: { - margin: 0, - padding: "0 0 0 18px", - }, - }; -} - -ControlOverviewResourceManager.propTypes = { - open: PropTypes.bool.isRequired, - onClose: PropTypes.func.isRequired, - resourceData: PropTypes.shape({ - ticket: PropTypes.string.isRequired, - expires: PropTypes.string.isRequired, - resources: PropTypes.arrayOf(PropTypes.string).isRequired, - controlId: PropTypes.number.isRequired, - resourceId: PropTypes.number.isRequired, - }), - onResourceExemptionDelete: PropTypes.func.isRequired, - onResourceExemptionSave: PropTypes.func.isRequired, -}; diff --git a/components/airview-compliance-ui/features/control-overview/control-overview.js b/components/airview-compliance-ui/features/control-overview/control-overview.js deleted file mode 100644 index 9ca2847f..00000000 --- a/components/airview-compliance-ui/features/control-overview/control-overview.js +++ /dev/null @@ -1,371 +0,0 @@ -import React, { useState, useMemo } from "react"; -import PropTypes from "prop-types"; -import { useTheme } from "@mui/material/styles"; -import { Paper, Box, Skeleton } from "@mui/material"; -import { ControlOverviewHeader } from "./control-overview-header"; -import { ControlOverviewGroup } from "./control-overview-group"; -import { ControlOverviewItem } from "./control-overview-item"; -import { Message } from "../../components"; -import { ControlOverviewItemDetail } from "./control-overview-item-detail"; -import { ControlOverviewItemResources } from "./control-overview-item-resources"; -import { ControlOverviewLoadingIndicator } from "./control-overview-loading-indicator"; -import { ControlOverviewResourceManager } from "./control-overview-resource-manager"; -import { ControlOverviewResourceEvidenceViewer } from "./control-overview-resource-evidence-viewer"; - -export function ControlOverview({ - loading, - title, - data, - onRequestOfControlsData, - onRequestOfResourcesData, - onResourceExemptionDelete, - onResourceExemptionSave, -}) { - const theme = useTheme(); - - const initialResourceManagerStatus = { - open: false, - controlId: null, - resourceId: null, - }; - - const initialResourceEvidenceStatus = { - open: false, - controlId: null, - resourceId: null, - }; - - const [exemptionManagerStatus, setExemptionManagerStatus] = useState({ - ...initialResourceManagerStatus, - }); - - const [resourceEvidenceStatus, setResourceEvidenceStatus] = useState({ - ...initialResourceEvidenceStatus, - }); - - const errorMessageFeedback = ( - - ); - - const noIssuesMessageFeedback = ( - - ); - - const invalidPermissionsMessageFeedback = ( - - ); - - const handleOnManageResourceClick = (controlId, resourceId) => { - setExemptionManagerStatus({ - open: true, - controlId, - resourceId, - }); - }; - - const handleOnResourceManagerClose = () => { - setExemptionManagerStatus({ ...initialResourceManagerStatus }); - }; - - const resourceManagerData = useMemo(() => { - if (!exemptionManagerStatus.open) return null; - - const resourcesData = - data.resources[exemptionManagerStatus.controlId].filter( - (resource) => resource.id === exemptionManagerStatus.resourceId - )[0]?.exemptionData ?? null; - - if (resourcesData) { - resourcesData.controlId = exemptionManagerStatus.controlId; - resourcesData.resourceId = exemptionManagerStatus.resourceId; - } - - return resourcesData; - }, [data, exemptionManagerStatus]); - - const handleOnViewResourceEvidenceClick = (controlId, resourceId) => { - setResourceEvidenceStatus({ - open: true, - controlId, - resourceId, - }); - }; - - const handleOnViewResourceEvidenceClose = () => { - setResourceEvidenceStatus({ ...initialResourceEvidenceStatus }); - }; - - const resourceEvidenceData = useMemo(() => { - if (!resourceEvidenceStatus.open) return null; - - return ( - data.resources[resourceEvidenceStatus.controlId].filter( - (resource) => resource.id === resourceEvidenceStatus.resourceId - )[0].evidence ?? null - ); - }, [data, resourceEvidenceStatus]); - - if (loading) { - return ; - } - - if (!data || data.groups === "loading") { - return ; - } - - if (!data.groups) { - return invalidPermissionsMessageFeedback; - } - - if (data.groups === "error") return errorMessageFeedback; - - if (Array.isArray(data.groups)) { - if (data.groups.length < 1) return noIssuesMessageFeedback; - - return ( - - - - - {data.groups.map((group) => { - return ( - - {(() => { - if ( - !data.controls || - !data.controls[group.id] || - data.controls[group.id] === "loading" - ) { - return ; - } - - if (data.controls[group.id] === "error") { - return ( - - - - ); - } - - if (Array.isArray(data.controls[group.id])) { - if (data.controls[group.id].length < 1) { - return ( - - - - ); - } - - return data.controls[group.id]?.map((control) => { - return ( - - - - {(() => { - if ( - !data.resources || - !data.resources[control.id] || - data.resources[control.id] === "loading" - ) { - return ; - } - - if (data.resources[control.id] === "error") { - return ( - - - - ); - } - - if (Array.isArray(data.resources[control.id])) { - if (data.resources[control.id].length < 1) { - return ( - - - - ); - } - - return ( - { - handleOnManageResourceClick( - control.id, - resourceId - ); - }} - onViewResourceEvidence={(resourceId) => { - handleOnViewResourceEvidenceClick( - control.id, - resourceId - ); - }} - /> - ); - } - })()} - - ); - }); - } - })()} - - ); - })} - - - - - - - ); - } -} - -ControlOverview.propTypes = { - /** - * Sets the component to render in a loading state - */ - loading: PropTypes.bool, - /** - * Sets the title for the component - */ - title: PropTypes.string, - /** - * Sets the required data to render the component UI - */ - data: PropTypes.shape({ - groups: PropTypes.oneOfType([ - PropTypes.arrayOf( - PropTypes.shape({ - id: PropTypes.number, - title: PropTypes.string, - }) - ), - PropTypes.oneOf(["error", "loading"]), - ]), - - controls: PropTypes.objectOf( - PropTypes.oneOfType([ - PropTypes.arrayOf( - PropTypes.shape({ - id: PropTypes.number, - name: PropTypes.string, - severity: PropTypes.oneOf(["Low", "Medium", "High"]), - applied: PropTypes.number, - exempt: PropTypes.number, - control: PropTypes.shape({ - name: PropTypes.string, - url: PropTypes.string, - }), - frameworks: PropTypes.arrayOf( - PropTypes.shape({ - name: PropTypes.string, - url: PropTypes.string, - }) - ), - qualityModel: PropTypes.string, - lifecycle: PropTypes.string, - }) - ), - PropTypes.oneOf(["error", "loading"]), - ]) - ), - - resources: PropTypes.objectOf( - PropTypes.oneOfType([ - PropTypes.arrayOf( - PropTypes.shape({ - id: PropTypes.number, - type: PropTypes.string, - reference: PropTypes.string, - environment: PropTypes.string, - lastSeen: PropTypes.string, - status: PropTypes.oneOf(["Monitoring", "Non-Compliant", "Exempt"]), - pending: PropTypes.bool, - exemptionData: PropTypes.shape({ - ticket: PropTypes.string.isRequired, - expires: PropTypes.string.isRequired, - resources: PropTypes.arrayOf(PropTypes.string).isRequired, - }), - }) - ), - PropTypes.oneOf(["error", "loading"]), - ]) - ), - }), - /** - * Callback for when a user expands a control group and a request is made to fetch the controls for that group. **Signature:** `function(groupId: int) => void` - */ - onRequestOfControlsData: PropTypes.func, - /** - * Callback for when a user expands a control within a given group and a request is made to fetch the resource data for that control. **Signature:** `function(controlId: int) => void` - */ - onRequestOfResourcesData: PropTypes.func, - /** - * Callback for when a user requests to delete a specific resource exemption. **Signature:** `function({controlId: Int, resourceId: Int}): Promise` - */ - onResourceExemptionDelete: PropTypes.func, - /** - * Callback for when a user requests to change the date of a specific resource exemption. **Signature:** `function({controlId: Int, resourceId: Int, revisedExpiryDate: ISO Date String}): Promise` - */ - onResourceExemptionSave: PropTypes.func, -}; diff --git a/components/airview-compliance-ui/features/control-overview/index.js b/components/airview-compliance-ui/features/control-overview/index.js deleted file mode 100644 index 967b932f..00000000 --- a/components/airview-compliance-ui/features/control-overview/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export { ControlOverview } from "./control-overview"; -export { useControlOverviewController } from "./use-control-overview-controller"; diff --git a/components/airview-compliance-ui/features/control-overview/use-control-overview-controller.js b/components/airview-compliance-ui/features/control-overview/use-control-overview-controller.js deleted file mode 100644 index 4edd84ad..00000000 --- a/components/airview-compliance-ui/features/control-overview/use-control-overview-controller.js +++ /dev/null @@ -1,86 +0,0 @@ -/* eslint react-hooks/exhaustive-deps: 0 */ -import { useEffect, useReducer } from "react"; - -function reducer(state, action) { - switch (action.type) { - case "INITIALIZE": { - return { - groups: action.payload, - controls: undefined, - resources: undefined, - }; - } - case "SET_CONTROLS_DATA": { - return { - ...state, - controls: { - ...state?.controls, - [action.id]: action.payload, - }, - }; - } - case "SET_RESOURCES_DATA": { - return { - ...state, - resources: { - ...state?.resources, - [action.id]: action.payload, - }, - }; - } - default: - throw new Error(`Unhandled action type: ${action.type}`); - } -} - -function useControlOverviewController(getInitalData, applicationId) { - const [state, dispatch] = useReducer(reducer); - - useEffect(() => { - if (applicationId === undefined) return; - initializeData(); - }, [applicationId]); - - async function initializeData() { - dispatch({ type: "INITIALIZE", payload: "loading" }); - - try { - const data = await getInitalData(); - dispatch({ type: "INITIALIZE", payload: data }); - } catch { - dispatch({ type: "INITIALIZE" }); - } - } - - async function setControlsData(id, getData) { - const groupDataValue = state?.controls?.[id]; - - if ( - groupDataValue !== "loading" && - (groupDataValue === undefined || groupDataValue === "error") - ) { - dispatch({ type: "SET_CONTROLS_DATA", id, payload: "loading" }); - - const data = await getData(); - dispatch({ type: "SET_CONTROLS_DATA", id, payload: data }); - } - } - - async function setResourcesData(id, getData) { - const resourcesDataValue = state?.resources?.[id]; - - if ( - resourcesDataValue !== "loading" && - (resourcesDataValue === undefined || resourcesDataValue === "error") - ) { - dispatch({ type: "SET_RESOURCES_DATA", id, payload: "loading" }); - - const data = await getData(); - dispatch({ type: "SET_RESOURCES_DATA", id, payload: data }); - } - } - - return [state, setControlsData, setResourcesData, initializeData]; -} - -export { useControlOverviewController }; diff --git a/components/airview-compliance-ui/features/control-overview/working-overlay.js b/components/airview-compliance-ui/features/control-overview/working-overlay.js deleted file mode 100644 index 502d9ede..00000000 --- a/components/airview-compliance-ui/features/control-overview/working-overlay.js +++ /dev/null @@ -1,47 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { CircularProgress, Box } from "@mui/material"; - -export function WorkingOverlay({ open, color, ...rest }) { - const classes = workingOverlayStyles(color); - - const { ...otherProps } = rest; - - if (!open) return null; - - return ( - - - - ); -} - -function workingOverlayStyles(color) { - return { - root: { - position: "absolute", - backgroundColor: "rgba(0,0,0, 0.1)", - top: 0, - right: 0, - bottom: 0, - left: 0, - display: "flex", - justifyContent: "center", - alignItems: "center", - }, - circle: { - color: color ?? "primary.main", - }, - }; -} - -WorkingOverlay.propTypes = { - /** - * Toggles the visibility of the component - */ - open: PropTypes.bool.isRequired, - /** - * Sets the color of the progress circle, defaults to theme.primary.main if not set. Accepts a valid [CSS color value](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value) - */ - color: PropTypes.string, -}; diff --git a/components/airview-compliance-ui/features/framework-view/framework-view-header.js b/components/airview-compliance-ui/features/framework-view/framework-view-header.js deleted file mode 100644 index 31de74b7..00000000 --- a/components/airview-compliance-ui/features/framework-view/framework-view-header.js +++ /dev/null @@ -1,32 +0,0 @@ -import React from "react"; -import { TableHead, TableRow, TableCell } from "@mui/material"; - -export function FrameworkViewHeader() { - const classes = frameworkViewHeaderStyles(); - - return ( - - - ID - - - Title - - - Owner - - Type - - - - - ); -} - -function frameworkViewHeaderStyles() { - return { - titleTableColumn: { - width: 350, - }, - }; -} diff --git a/components/airview-compliance-ui/features/framework-view/framework-view-row-detail.js b/components/airview-compliance-ui/features/framework-view/framework-view-row-detail.js deleted file mode 100644 index 364909f5..00000000 --- a/components/airview-compliance-ui/features/framework-view/framework-view-row-detail.js +++ /dev/null @@ -1,120 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { Box, Chip } from "@mui/material"; - -export function FrameworkViewRowDetail({ detailData }) { - const classes = frameworkRowDetailStyles(); - - return ( - - - - - Detail: - - {detailData.detail} - - - - - - Control Mapping: - - - {detailData.controlMappings?.map((controlMappings, index) => { - return ( - - ); - })} - - - - - - Frequency: - - - - - - - - - ); -} - -FrameworkViewRowDetail.propTypes = { - detailData: PropTypes.object.isRequired, -}; - -function frameworkRowDetailStyles() { - return { - additionalInfoContainer: { - borderTop: 1, - borderTopColor: "divider", - display: "flex", - justifyContent: "space-between", - }, - - instanceInfo: { - listStyle: "none", - padding: 2, - margin: 0, - flex: "1 1 74.5%", - }, - - instanceInfoItem: { - padding: 1, - display: "flex", - - "& > span:first-of-type": { - fontWeight: "bold", - }, - }, - - frequency: { - borderRadius: 1, - flex: "1 1 auto", - maxWidth: 250, - fontWeight: "bold", - }, - - controlMapping: { - margin: 0.5, - borderRadius: 1, - flex: "1 1 auto", - maxWidth: 250, - fontWeight: "bold", - }, - - controlMappingIconGap_no: { - backgroundColor: "success.light", - color: "success.contrastText", - }, - - controlMappingIconGap_partial: { - backgroundColor: "warning.light", - color: "warning.contrastText", - }, - - controlMappingIconGap_full: { - backgroundColor: "grey.500", - color: "white", - }, - }; -} diff --git a/components/airview-compliance-ui/features/framework-view/framework-view-row.js b/components/airview-compliance-ui/features/framework-view/framework-view-row.js deleted file mode 100644 index 2687803b..00000000 --- a/components/airview-compliance-ui/features/framework-view/framework-view-row.js +++ /dev/null @@ -1,75 +0,0 @@ -import React, { useState } from "react"; -import PropTypes from "prop-types"; -import { TableRow, TableCell, Collapse, Box, IconButton } from "@mui/material"; -import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; -import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp"; - -export function FrameworkViewRow({ - controlID, - controlTitle, - owner, - type, - children, -}) { - const classes = frameworkViewRowStyles(); - const [open, setOpen] = useState(false); - - return ( - - - - {controlID} - - - - {controlTitle} - - - - {owner} - - - - {type} - - - - setOpen(!open)} - > - {open ? : } - - - - - - - - {children} - - - - - ); -} - -FrameworkViewRow.propTypes = { - controlID: PropTypes.string.isRequired, - controlTitle: PropTypes.string.isRequired, - owner: PropTypes.string.isRequired, - type: PropTypes.string.isRequired, - children: PropTypes.node, -}; - -function frameworkViewRowStyles() { - return { - // Table body rows - tableBodyRowRoot: { - "& > *": { - borderBottom: "unset", - }, - }, - }; -} diff --git a/components/airview-compliance-ui/features/framework-view/framework-view-toolbar.js b/components/airview-compliance-ui/features/framework-view/framework-view-toolbar.js deleted file mode 100644 index cdc4c947..00000000 --- a/components/airview-compliance-ui/features/framework-view/framework-view-toolbar.js +++ /dev/null @@ -1,36 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { Toolbar, Typography } from "@mui/material"; - -export function FrameworkViewToolbar({ title, testid }) { - const classes = frameworkViewToolbarStyles(); - - if (!title) return null; - - return ( - - {title && ( - - {title} - - )} - - ); -} - -FrameworkViewToolbar.propTypes = { - title: PropTypes.string, - testid: PropTypes.string, -}; - -function frameworkViewToolbarStyles() { - return { - toolbar: { - paddingLeft: 2, - paddingRight: 1, - }, - }; -} diff --git a/components/airview-compliance-ui/features/framework-view/framework-view.js b/components/airview-compliance-ui/features/framework-view/framework-view.js deleted file mode 100644 index 505f2a4b..00000000 --- a/components/airview-compliance-ui/features/framework-view/framework-view.js +++ /dev/null @@ -1,122 +0,0 @@ -import React, { useMemo } from "react"; -import PropTypes from "prop-types"; -import { useTheme } from "@mui/material/styles"; -import { - Paper, - Table, - TableContainer, - TableBody, - Skeleton, -} from "@mui/material"; -import { FrameworkViewToolbar } from "./framework-view-toolbar"; -import { FrameworkViewHeader } from "./framework-view-header"; -import { FrameworkViewRow } from "./framework-view-row"; -import { FrameworkViewRowDetail } from "./framework-view-row-detail"; -import { Message } from "../../components"; - -export function FrameworkView({ - title, - applications, - loading, - noDataMessage, - invalidPermissionsMessage, -}) { - const theme = useTheme(); - - const getProcessedApplicationData = useMemo(() => { - if (loading || !applications) return; - - return applications; - }, [applications, loading]); - - if (loading) { - return ; - } - - if (!applications) { - return ( - - ); - } - - if (applications.length < 1) { - return ( - - ); - } - - return ( - - - - - - - - {getProcessedApplicationData.map((application) => ( - - - - ))} - -
-
- ); -} - -FrameworkView.propTypes = { - /** - * Presents the component in a lodaing state (for when fetching data async) - */ - loading: PropTypes.bool, - /** - * An optional title for the table - */ - title: PropTypes.string, - /** - * The collection of applications to render to the complianceTable, should be an array of applications, an empty array (for no issues) of null (for invalid permissions) - */ - applications: PropTypes.arrayOf( - PropTypes.shape({ - id: PropTypes.number.isRequired, - controlID: PropTypes.string.isRequired, - controlTitle: PropTypes.string.isRequired, - owner: PropTypes.string.isRequired, - type: PropTypes.string.isRequired, - applicationDetailData: PropTypes.shape({ - detail: PropTypes.string, - controlMappings: PropTypes.arrayOf( - PropTypes.shape({ - name: PropTypes.string.isRequired, - gap: PropTypes.string.isRequired, - }) - ), - Frequency: PropTypes.string, - }), - }) - ), - /** - * Used to display a message to the user when they do not have required permissions to view the application data - */ - invalidPermissionsMessage: PropTypes.shape({ - title: PropTypes.string.isRequired, - message: PropTypes.string.isRequired, - }).isRequired, - /** - * Used to display a message to the user when there is no data to display for the application - */ - noDataMessage: PropTypes.shape({ - title: PropTypes.string.isRequired, - message: PropTypes.string.isRequired, - }).isRequired, -}; diff --git a/components/airview-compliance-ui/features/framework-view/index.js b/components/airview-compliance-ui/features/framework-view/index.js deleted file mode 100644 index eaacadf2..00000000 --- a/components/airview-compliance-ui/features/framework-view/index.js +++ /dev/null @@ -1 +0,0 @@ -export { FrameworkView } from "./framework-view"; diff --git a/components/airview-compliance-ui/features/icon-chip/icon-chip.js b/components/airview-compliance-ui/features/icon-chip/icon-chip.js deleted file mode 100644 index 8f84baaa..00000000 --- a/components/airview-compliance-ui/features/icon-chip/icon-chip.js +++ /dev/null @@ -1,101 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { Box } from "@mui/material"; - -const IconChip = React.forwardRef( - ( - { - icon, - label, - color = "#000", - labelColor = "#fff", - dense = false, - ...rest - }, - ref - ) => { - const classes = useIconChipStyles(color, labelColor, dense); - - const { ...otherProps } = rest; - - return ( - - - {icon} - - - {label} - - - ); - } -); - -IconChip.displayName = "IconChip"; - -IconChip.propTypes = { - /** - * The icon to display in the chip, accepts one [Material-UI SVG Icon](https://material-ui.com/components/material-icons/) - */ - icon: PropTypes.node.isRequired, - /** - * The content of the chip label text - */ - label: PropTypes.string.isRequired, - /** - * The color of the chip, should be a valid [CSS color value](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value) - */ - color: PropTypes.string, - /** - * The color of the chip label, should be a valid [CSS color value](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value) - */ - labelColor: PropTypes.string, - /** - * Reduces the padding of around the chip icon and label for a smaller appearance - */ - dense: PropTypes.bool, -}; - -function useIconChipStyles(color, labelColor, dense) { - return { - root: { - display: "inline-flex", - justifyContent: "space-between", - border: "1px solid", - borderColor: color, - borderRadius: 1, - backgroundColor: "#fff", - }, - - iconContainer: { - display: "flex", - justifyContent: "center", - alignItems: "center", - py: dense ? 0.25 : 0.5, - px: dense ? 0.5 : 0.75, - color: color, - "& > .MuiSvgIcon-root": { - fontSize: 16, - }, - }, - - labelContainer: { - display: "flex", - justifyContent: "center", - alignItems: "center", - py: dense ? 0.25 : 0.5, - px: dense ? 0.5 : 0.75, - backgroundColor: color, - color: labelColor, - typography: "body1", - fontWeight: "bold", - textAlign: "left", - minWidth: dense ? 24 : 30, - minHeight: 24, - fontSize: 14, - lineHeight: 1.43, - }, - }; -} - -export { IconChip }; diff --git a/components/airview-compliance-ui/features/icon-chip/index.js b/components/airview-compliance-ui/features/icon-chip/index.js deleted file mode 100644 index cd72fe94..00000000 --- a/components/airview-compliance-ui/features/icon-chip/index.js +++ /dev/null @@ -1 +0,0 @@ -export { IconChip } from "./icon-chip"; diff --git a/components/airview-compliance-ui/features/index.js b/components/airview-compliance-ui/features/index.js deleted file mode 100644 index 70f16d0a..00000000 --- a/components/airview-compliance-ui/features/index.js +++ /dev/null @@ -1,6 +0,0 @@ -export * from "./application-tile"; -export * from "./compliance-table"; -export * from "./control-overview"; -export * from "./icon-chip"; -export * from "./progress-bar"; -export * from "./framework-view"; diff --git a/components/airview-compliance-ui/features/link/index.js b/components/airview-compliance-ui/features/link/index.js deleted file mode 100644 index f7f96c3f..00000000 --- a/components/airview-compliance-ui/features/link/index.js +++ /dev/null @@ -1 +0,0 @@ -export { Link } from "./link"; diff --git a/components/airview-compliance-ui/features/link/link.js b/components/airview-compliance-ui/features/link/link.js deleted file mode 100644 index 9bfbc110..00000000 --- a/components/airview-compliance-ui/features/link/link.js +++ /dev/null @@ -1,87 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { HashLink } from "react-router-hash-link"; -import { Link as MuiLink } from "@mui/material"; -import clsx from "clsx"; -import { useLocation } from "../../hooks/use-location"; - -export const Link = React.forwardRef( - ({ href, noLinkStyle = false, activeClassName, children, ...rest }, ref) => { - const { className, ...otherProps } = rest; - - const location = useLocation(); - - const internal = /^\/(?!\/)/.test(href); - const isFile = /\.[0-9a-z]+$/i.test(href); - - if (!internal || isFile) { - if (noLinkStyle) { - return ( - - {children} - - ); - } - - return ( - - {children} - - ); - } - - const classes = clsx(className, href === location ? activeClassName : null); - - if (noLinkStyle) { - return ( - - {children} - - ); - } - - return ( - - {children} - - ); - } -); - -Link.displayName = "Link"; - -Link.propTypes = { - /** - * Sets the href for the link - */ - href: PropTypes.string.isRequired, - /** - * Renders the link with no MUI Link styles - */ - noLinkStyle: PropTypes.bool, - /** - * Applies an active classname to the Link (if required) - */ - activeClassName: PropTypes.string, - /** - * Used to set the inner content of the link - */ - children: PropTypes.node.isRequired, -}; diff --git a/components/airview-compliance-ui/features/progress-bar/index.js b/components/airview-compliance-ui/features/progress-bar/index.js deleted file mode 100644 index 5d1a89ff..00000000 --- a/components/airview-compliance-ui/features/progress-bar/index.js +++ /dev/null @@ -1 +0,0 @@ -export { ProgressBar } from "./progress-bar"; diff --git a/components/airview-compliance-ui/features/progress-bar/progress-bar.js b/components/airview-compliance-ui/features/progress-bar/progress-bar.js deleted file mode 100644 index 9df2b4e7..00000000 --- a/components/airview-compliance-ui/features/progress-bar/progress-bar.js +++ /dev/null @@ -1,108 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { LinearProgress, Typography, Box } from "@mui/material"; -import { lighten } from "@mui/material/styles"; - -export function ProgressBar({ - value, - showLabel = true, - color = "#000", - variant = "determinate", - ariaLabel, - classNames, -}) { - const classes = useProgressBarStyles(color); - - return ( - - - - - - {showLabel && ( - - {`${Math.round(value)}%`} - - )} - - ); -} - -ProgressBar.propTypes = { - /** - * The value of the progress indicator for the determinate and buffer variants. Value between 0 and 100. - */ - value: PropTypes.number, - /** - * The color for the progress bar, should be a valid [CSS color value](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value) - */ - color: PropTypes.string, - /** - * The variant to use. Use "indeterminate" when there is no progress value. - */ - variant: PropTypes.oneOf(["determinate", "indeterminate"]), - /** - * Displays or hides the progress percentile value label, set to false when using variant "indeterminate" - */ - showLabel: PropTypes.bool, - /** - * An accesible label for the progress bar - */ - ariaLabel: PropTypes.string, - /** - * Allows the passing of additional style classes to the component root node - */ - classNames: PropTypes.string, -}; - -function useProgressBarStyles(color) { - return { - lpRoot: { - borderRadius: 1, - height: 12, - backgroundColor: lighten(color, 0.5), - }, - lpBar: { - backgroundColor: color, - }, - label: { - lineHeight: 0, - }, - }; -} - -/* -ApplicationTile - Children: node - -ApplicationTile.Header - Children: node - Size: enum ["regular, small"] - -ApplicationTile.Content - Children: node - Collapsible: bool - InitialCollapsed: bool - -ApplicationTile.ContentRow - children: node - -ApplicationTile.Title - children: node -*/ diff --git a/components/airview-compliance-ui/hooks/use-location/index.js b/components/airview-compliance-ui/hooks/use-location/index.js deleted file mode 100644 index 9958a006..00000000 --- a/components/airview-compliance-ui/hooks/use-location/index.js +++ /dev/null @@ -1 +0,0 @@ -export { LocationProvider, useLocation } from "./use-location"; diff --git a/components/airview-compliance-ui/hooks/use-location/use-location.js b/components/airview-compliance-ui/hooks/use-location/use-location.js deleted file mode 100644 index ee0afae0..00000000 --- a/components/airview-compliance-ui/hooks/use-location/use-location.js +++ /dev/null @@ -1,19 +0,0 @@ -import React, { useContext } from "react"; -import PropTypes from "prop-types"; - -const LocationContext = React.createContext(); - -export function LocationProvider({ location, children }) { - return ( - - {children} - - ); -} - -LocationProvider.propTypes = { - location: PropTypes.string, - children: PropTypes.node, -}; - -export const useLocation = () => useContext(LocationContext); diff --git a/components/airview-compliance-ui/index.js b/components/airview-compliance-ui/index.js deleted file mode 100644 index 20b863c1..00000000 --- a/components/airview-compliance-ui/index.js +++ /dev/null @@ -1 +0,0 @@ -export * from "@features"; diff --git a/components/airview-compliance-ui/stories/application-tile/application-tile.docs.md b/components/airview-compliance-ui/stories/application-tile/application-tile.docs.md deleted file mode 100644 index c94d057f..00000000 --- a/components/airview-compliance-ui/stories/application-tile/application-tile.docs.md +++ /dev/null @@ -1,22 +0,0 @@ -The `ApplicationTile` component uses a compositional pattern, allowing you to have control of the layout and content via the use of available sub-components. Namely: - -- ApplicationTileHeader -- ApplicationTileTitle -- ApplicationTileContent -- ApplicationTileContentRow -- ApplicationTileDivider - -A detailed API documentation for these sub-components can be viewed in the accompanying sibling stories. - -The `ApplicationTile` component exposes an optional `gutter` prop, which can be used to apply bottom margin to the root node. This can be used when nesting `ApplicationTile` components. - -**Importing the components** - -```javascript -import { ApplicationTile } from "@/components/application-tile"; -import { ApplicationTileHeader } from "@/components/application-tile"; -import { ApplicationTileTitle } from "@/components/application-tile"; -import { ApplicationTileContent } from "@/components/application-tile"; -import { ApplicationTileContentRow } from "@/components/application-tile"; -import { ApplicationTileDivider } from "@/components/application-tile"; -``` diff --git a/components/airview-compliance-ui/stories/application-tile/application-tile.stories.js b/components/airview-compliance-ui/stories/application-tile/application-tile.stories.js deleted file mode 100644 index a57899fb..00000000 --- a/components/airview-compliance-ui/stories/application-tile/application-tile.stories.js +++ /dev/null @@ -1,319 +0,0 @@ -import React from "react"; -import { userEvent, within } from "@storybook/testing-library"; -import WarningIcon from "@mui/icons-material/Warning"; -import { Typography, Box } from "@mui/material"; -import { - ApplicationTile, - ApplicationTileHeader, - ApplicationTileTitle, - ApplicationTileDivider, - ApplicationTileContent, - ApplicationTileContentRow, - ApplicationTileCallToActionButton, - ApplicationTileChip, -} from "../../features/application-tile"; -import { ProgressBar } from "../../features/progress-bar"; -import docs from "./application-tile.docs.md"; - -export default { - title: "Features/Application Tile", - component: ApplicationTile, - subcomponents: { - ApplicationTileHeader, - ApplicationTileTitle, - ApplicationTileContent, - ApplicationTileContentRow, - ApplicationTileCallToActionButton, - ApplicationTileChip, - }, - parameters: { - docs: { - description: { - component: docs, - }, - }, - controls: false, - layout: "padded", - }, - decorators: [ - (story) => { - const classes = { - root: { - width: "100%", - maxWidth: 360, - margin: "0 auto", - flex: "1 1 auto", - }, - }; - - return {story()}; - }, - ], -}; - -export const WithNoData = () => { - return ( - - Application One - } - rightContent={ - - } - /> - - - - You do not have the required permissions to view this data - - - - - ); -}; - -export const WithDataNotCollapsible = () => { - return ( - - Application One - } - rightContent={ - - } - /> - - - - Production - - - } - label="2" - dense - color="success.main" - /> - - } - label="2" - dense - color="warning.main" - /> - - } - label="2" - dense - color="error.main" - /> - - } - label="2" - dense - color="grey.800" - /> - - - - - - - ); -}; - -export const WithDataCollapsed = () => { - return ( - - Application One - } - rightContent={ - - } - /> - - - - - Production - - - - - - UAT - - - - - - Development - - - - - - - - - - - Production - } - /> - - - - } - label="2" - dense - color="success.main" - /> - - } - label="2" - dense - color="warning.main" - /> - - } - label="2" - dense - color="error.main" - /> - - } - label="2" - dense - color="grey.800" - /> - - - - - - UAT - } - /> - - - - } - label="2" - dense - color="success.main" - /> - - } - label="2" - dense - color="warning.main" - /> - - } - label="2" - dense - color="error.main" - /> - - } - label="2" - dense - color="grey.800" - /> - - - - - - - Development - - } - /> - - - - } - label="2" - dense - color="success.main" - /> - - } - label="2" - dense - color="warning.main" - /> - - } - label="2" - dense - color="error.main" - /> - - } - label="2" - dense - color="grey.800" - /> - - - - - - ); -}; - -export const WithDataExpanded = WithDataCollapsed.bind({}); - -WithDataExpanded.play = async ({ canvasElement }) => { - const canvas = within(canvasElement); - - await userEvent.click( - canvas.getByRole("button", { name: /expand content/i }) - ); -}; diff --git a/components/airview-compliance-ui/stories/compliance-table/applications-data.js b/components/airview-compliance-ui/stories/compliance-table/applications-data.js deleted file mode 100644 index b1443add..00000000 --- a/components/airview-compliance-ui/stories/compliance-table/applications-data.js +++ /dev/null @@ -1,465 +0,0 @@ -export const applicationsDataOld = [ - { - id: 1, - severity: "medium", - name: "Vivamus vestibulum ntulla nec ante", - tickets: [ - { - reference: "PRB12345678", - type: "problem", - }, - ], - raisedDateTime: (function () { - const now = new Date(); - now.setHours(now.getHours() - 1); - return now.toISOString(); - })(), - environment: "Production", - applicationDetailData: { - instances: [ - { - id: 1, - name: "Server Instance 1", - status: "none", - }, - { - id: 2, - name: "Server Instance 2", - status: "none", - }, - { - id: 3, - name: "Server Instance 3", - status: "none", - }, - { - id: 4, - name: "Server Instance 4", - status: "none", - }, - { - id: 5, - name: "Server Instance 5", - status: "none", - }, - { - id: 6, - name: "Server Instance 6", - status: "none", - }, - { - id: 7, - name: "Server Instance 7", - status: "none", - }, - { - id: 8, - name: "Server Instance 8", - status: "none", - }, - { - id: 9, - name: "Server Instance 9", - status: "none", - }, - { - id: 10, - name: "Server Instance 10", - status: "none", - }, - { - id: 11, - name: "Server Instance 11", - status: "none", - }, - { - id: 12, - name: "Server Instance 12", - status: "none", - }, - { - id: 13, - name: "Server Instance 13", - status: "none", - }, - { - id: 14, - name: "Server Instance 14", - status: "none", - }, - { - id: 15, - name: "Server Instance 15", - status: "none", - }, - { - id: 16, - name: "Server Instance 16", - status: "none", - }, - ], - control: { - name: "AWS ECS Pattern CPU Monitoring", - url: "/", - }, - frameworks: [ - { - name: "PCI-DSS", - url: "/", - }, - { - name: "ISO27K", - url: "/", - }, - { - name: "GPK", - url: "/", - }, - ], - environment: "production", - assignmentGroup: "Server Team", - assignee: "James Brown", - systemSource: "AWS/CCF", - systemStage: "Monitor", - }, - }, - { - id: 2, - - severity: "high", - name: "Donec consectetuer ligula vulputate sem tristique cursus", - tickets: [ - { - reference: "INC12345678", - type: "incident", - }, - ], - raisedDateTime: (function () { - const now = new Date(); - now.setMinutes(now.getMinutes() - 10); - return now.toISOString(); - })(), - environment: "Production", - applicationDetailData: { - instances: [ - { - id: 1, - name: "Server Instance 1", - status: "pending", - }, - ], - control: { - name: "AWS ECS Pattern CPU Monitoring", - url: "/", - }, - frameworks: [ - { - name: "PCI-DSS", - url: "/", - }, - { - name: "ISO27K", - url: "/", - }, - { - name: "GPK", - url: "/", - }, - ], - environment: "production", - assignmentGroup: "Server Team", - assignee: "James Brown", - systemSource: "AWS/CCF", - systemStage: "Monitor", - }, - }, - { - id: 3, - - severity: "low", - name: "Integer vitae libero ac risus egestas placerat", - tickets: [ - { - reference: "INC12345678-ABT", - type: "incident", - }, - { - reference: "PRB12345678", - type: "problem", - }, - { - reference: - "RSK12345678QJDTANDKRSK12345678QJDTANDKRSK12345678QJDTANDKRS", - type: "risk", - }, - ], - raisedDateTime: (function () { - const now = new Date(); - now.setDate(now.getDate() - 1); - return now.toISOString(); - })(), - environment: "UAT", - applicationDetailData: { - instances: [ - { - id: 1, - name: "Server Instance 1", - status: "pending", - }, - ], - control: { - name: "AWS ECS Pattern CPU Monitoring", - url: "/", - }, - frameworks: [ - { - name: "PCI-DSS", - url: "/", - }, - { - name: "ISO27K", - url: "/", - }, - { - name: "GPK", - url: "/", - }, - ], - environment: "production", - assignmentGroup: "Server Team", - assignee: "James Brown", - systemSource: "AWS/CCF", - systemStage: "Monitor", - }, - }, - { - id: 4, - - severity: "low", - name: "Vivamus id diam bibendum, rhoncus leo quis, consequat", - tickets: [ - { - reference: "INC12345678-ABT", - type: "incident", - }, - ], - raisedDateTime: (function () { - const now = new Date(); - now.setMonth(now.getMonth() - 1); - return now.toISOString(); - })(), - environment: "UAT", - applicationDetailData: { - instances: [ - { - id: 1, - name: "Server Instance 1", - status: "pending", - }, - ], - control: { - name: "AWS ECS Pattern CPU Monitoring", - url: "/", - }, - frameworks: [ - { - name: "PCI-DSS", - url: "/", - }, - { - name: "ISO27K", - url: "/", - }, - { - name: "GPK", - url: "/", - }, - ], - environment: "production", - assignmentGroup: "Server Team", - assignee: "James Brown", - systemSource: "AWS/CCF", - systemStage: "Monitor", - }, - }, - { - id: 5, - - severity: "medium", - name: "Phasellus fermentum tincidunt nisl", - tickets: [ - { - reference: "PRB12345678", - type: "problem", - }, - ], - raisedDateTime: (function () { - const now = new Date(); - now.setFullYear(now.getFullYear() - 1); - return now.toISOString(); - })(), - environment: "UAT", - applicationDetailData: { - instances: [ - { - id: 1, - name: "Server Instance 1", - status: "pending", - }, - ], - control: { - name: "AWS ECS Pattern CPU Monitoring", - url: "/", - }, - frameworks: [ - { - name: "PCI-DSS", - url: "/", - }, - { - name: "ISO27K", - url: "/", - }, - { - name: "GPK", - url: "/", - }, - ], - environment: "production", - assignmentGroup: "Server Team", - assignee: "James Brown", - systemSource: "AWS/CCF", - systemStage: "Monitor", - }, - }, -]; - -export const applicationsData = [ - { - id: 1, - severity: "medium", - technicalControlName: "Vivamus vestibulum ntulla nec ante", - tickets: [{ reference: "PRB12345678", type: "problem" }], - raisedDateTime: "2023-06-23T10:19:15.832Z", - environment: "Production", - resources: [ - { id: 1, name: "Server Instance 1", status: "none" }, - { id: 2, name: "Server Instance 2", status: "none" }, - { id: 3, name: "Server Instance 3", status: "none" }, - { id: 4, name: "Server Instance 4", status: "none" }, - { id: 5, name: "Server Instance 5", status: "none" }, - { id: 6, name: "Server Instance 6", status: "none" }, - { id: 7, name: "Server Instance 7", status: "none" }, - { id: 8, name: "Server Instance 8", status: "none" }, - { id: 9, name: "Server Instance 9", status: "none" }, - { id: 10, name: "Server Instance 10", status: "none" }, - { id: 11, name: "Server Instance 11", status: "none" }, - { id: 12, name: "Server Instance 12", status: "none" }, - { id: 13, name: "Server Instance 13", status: "none" }, - { id: 14, name: "Server Instance 14", status: "none" }, - { id: 15, name: "Server Instance 15", status: "none" }, - { id: 16, name: "Server Instance 16", status: "none" }, - ], - control: "AWS ECS Pattern CPU Monitoring", - frameworks: [ - { name: "PCI-DSS", url: "/" }, - { name: "ISO27K", url: "/" }, - { name: "GPK", url: "/" }, - ], - assignmentGroup: "Server Team", - assignee: "James Brown", - systemName: "AWS/CCF", - systemStage: "Monitor", - }, - { - id: 2, - severity: "high", - technicalControlName: - "Donec consectetuer ligula vulputate sem tristique cursus", - tickets: [{ reference: "INC12345678", type: "incident" }], - raisedDateTime: "2023-06-23T11:09:15.833Z", - environment: "Production", - resources: [{ id: 1, name: "Server Instance 1", status: "pending" }], - control: "AWS ECS Pattern CPU Monitoring", - frameworks: [ - { name: "PCI-DSS", url: "/" }, - { name: "ISO27K", url: "/" }, - { name: "GPK", url: "/" }, - ], - assignmentGroup: "Server Team", - assignee: "James Brown", - systemName: "AWS/CCF", - systemStage: "Monitor", - }, - { - id: 3, - severity: "low", - technicalControlName: "Integer vitae libero ac risus egestas placerat", - tickets: [ - { reference: "INC12345678-ABT", type: "incident" }, - { reference: "PRB12345678", type: "problem" }, - { - reference: - "RSK12345678QJDTANDKRSK12345678QJDTANDKRSK12345678QJDTANDKRS", - type: "risk", - }, - ], - raisedDateTime: "2023-06-22T11:19:15.833Z", - environment: "UAT", - resources: [{ id: 1, name: "Server Instance 1", status: "pending" }], - control: "AWS ECS Pattern CPU Monitoring", - frameworks: [ - { name: "PCI-DSS", url: "/" }, - { name: "ISO27K", url: "/" }, - { name: "GPK", url: "/" }, - ], - assignmentGroup: "Server Team", - assignee: "James Brown", - systemName: "AWS/CCF", - systemStage: "Monitor", - }, - { - id: 4, - severity: "low", - technicalControlName: - "Vivamus id diam bibendum, rhoncus leo quis, consequat", - tickets: [{ reference: "INC12345678-ABT", type: "incident" }], - raisedDateTime: "2023-05-23T11:19:15.833Z", - environment: "UAT", - resources: [{ id: 1, name: "Server Instance 1", status: "pending" }], - control: "AWS ECS Pattern CPU Monitoring", - frameworks: [ - { name: "PCI-DSS", url: "/" }, - { name: "ISO27K", url: "/" }, - { name: "GPK", url: "/" }, - ], - assignmentGroup: "Server Team", - assignee: "James Brown", - systemName: "AWS/CCF", - systemStage: "Monitor", - }, - { - id: 5, - severity: "medium", - technicalControlName: "Phasellus fermentum tincidunt nisl", - // tickets: [{ reference: "PRB12345678", type: "problem" }], - tickets: [], - raisedDateTime: "2022-06-23T11:19:15.833Z", - environment: "UAT", - resources: [{ id: 1, name: "Server Instance 1", status: "pending" }], - control: "AWS ECS Pattern CPU Monitoring", - frameworks: [ - { name: "PCI-DSS", url: "/" }, - { name: "ISO27K", url: "/" }, - { name: "GPK", url: "/" }, - ], - assignmentGroup: "Server Team", - assignee: "James Brown", - systemName: "AWS/CCF", - systemStage: "Monitor", - }, -]; - -export async function getApplicationsData() { - return applicationsData; -} diff --git a/components/airview-compliance-ui/stories/compliance-table/compliance-table.stories.js b/components/airview-compliance-ui/stories/compliance-table/compliance-table.stories.js deleted file mode 100644 index c2f25326..00000000 --- a/components/airview-compliance-ui/stories/compliance-table/compliance-table.stories.js +++ /dev/null @@ -1,90 +0,0 @@ -import React from "react"; -import { ComplianceTable } from "../../features/compliance-table"; -import { applicationsData } from "./applications-data"; -import { Box } from "@mui/material"; - -export default { - title: "Features/Compliance Table", - component: ComplianceTable, - parameters: { - layout: "padded", - }, - decorators: [ - (story) => { - return ( - - {story()} - - ); - }, - ], -}; - -function Template(args) { - return ; -} - -Template.args = { - title: "Compliance Table", - noDataMessage: { - title: "No issues", - message: "There are no issues to display for this application", - }, - invalidPermissionsMessage: { - title: "Notice", - message: - "You do not have the required permissions to view the data for this application", - }, -}; - -export const Loading = { - ...Template, - args: { - ...Template.args, - loading: true, - applications: [], - }, -}; - -export const LoadedWithSingleIssue = { - ...Template, - args: { - ...Template.args, - loading: false, - applications: [applicationsData[0]], - }, -}; - -export const LoadedWithMultipleIssues = { - ...Template, - args: { - ...Template.args, - loading: false, - applications: applicationsData, - }, -}; - -export const LoadedWithNoIssues = { - ...Template, - args: { - ...Template.args, - loading: false, - applications: [], - }, -}; - -export const WithoutRequiredPermissions = { - ...Template, - args: { - ...Template.args, - loading: false, - applications: null, - }, -}; - -/* - -WithIssues -WithoutIssues -WithoutRequiredPermissions -*/ diff --git a/components/airview-compliance-ui/stories/control-overview/control-overview.docs.md b/components/airview-compliance-ui/stories/control-overview/control-overview.docs.md deleted file mode 100644 index 9b6206ae..00000000 --- a/components/airview-compliance-ui/stories/control-overview/control-overview.docs.md +++ /dev/null @@ -1,109 +0,0 @@ -The `ControlOverview` is a presentational component, that is complimented with an optional controller `useControlOverviewController`, which can be used for state management of group, control and resources data. - -## Importing the component and controller - -```javascript -import { - ControlOverview, - useControlOverviewController, -} from "@/components/control-overview"; -``` - -## Providing data to the component - -The data object used to populate the component is intented to work as a relational table of sub-data, with no data set, the component defaults to a loading state. - -The top level data keys are: - -- `groups` : Used to populate control groupings within the UI; the data should be an array of groups or a string, to indicate either an error or loading status. Setting the data to an empty array indicates there are no issues for the component to display. -- `controls` : Used to store data for controls; the data should be an object of arrays or a string, to indicate either an error or loading status. The object key should be equal to the group id the data relates to; setting the data to an empty array indicates there are no issues for the specific control. -- `resources` Used to store resource data for a specific control; the data should be an object of arrays or a string, to indicate either an error or loading status. The object key should be equal to the control id the data relates to; setting the data to an empty array indicates there are no resources for the specific control. - -## Using the optional controller - -The component comes with an optional controller as a custom React hook, that can be used to manage component state and the asynchronous retrival of the required data. - -### Initialising - -The controller hook accepts one parameter (`function`), to act as an mechanism fetch the initial group data. The function should return data equal to the `groups` key within the `data` prop: - -```javascript -[ - { - id: number, - title: string - } -] | 'error', -``` - -### Return values - -The controller hook will return an array containing: - -#### State - -An object of the current state, used to feed the `data` prop of the `ControlOverview` component. - -#### Controls data setter - -A function to set the contol data that accepts two parameters, group id (`int`) and an asynchronous function used to fetch control data specific to the requested group. - -This callback to fetch the control data will not be fired if control data already exists for a given group id. - -The function should return data equal to the `controls` key within the `data` prop: - -```javascript -[ - { - id: number, - name: string, - severity: "Low" | "Medium" | "High", - applied: number, - exempt: number, - control: { - name: string, - url: string, - }, - frameworks: [ - { - name: string, - url: string, - }, - ], - qualityModel: string, - lifecycle: string, - }, -] | "error"; -``` - -#### Resources data setter - -A function to set the resources data that accepts two parameters, control id (`int`) and an asynchronous function used to fetch resources data specific to the requested control id. - -This callback to fetch the resources data will not be fired if resources data already exists for a given control id. - -The function should return data equal to the `resources` key within the `data` prop: - -```javascript -[ - { - id: number, - type: string, - reference: string, - environment: string, - lastSeen: string, - status: "Monitoring" | "Non-Compliant" | "Exempt", - pending: bool, - exemptionData: { - ticket: string, - expires: string // ISO Date String - resources: [string] - }, - evidence: string // Markdown - }, -] | "error"; -``` - -#### Initialiser - -A function to reset the component data back to it's intial state and re-fetch the data. Internally this calls the same function passed when invoking the hook. diff --git a/components/airview-compliance-ui/stories/control-overview/control-overview.stories.js b/components/airview-compliance-ui/stories/control-overview/control-overview.stories.js deleted file mode 100644 index 37eae8ed..00000000 --- a/components/airview-compliance-ui/stories/control-overview/control-overview.stories.js +++ /dev/null @@ -1,166 +0,0 @@ -import React from "react"; -import { - ControlOverview, - useControlOverviewController, -} from "../../features/control-overview"; -import docs from "./control-overview.docs.md"; -import { groups, controls, resources } from "./data"; -import { Box } from "@mui/material"; - -export default { - title: "Features/Control Overview", - component: ControlOverview, - decorators: [ - (story) => { - const classes = { - root: { - width: "100%", - maxWidth: 1024, - margin: "0 auto", - flex: 1, - }, - }; - - return {story()}; - }, - ], - parameters: { - docs: { - description: { - component: docs, - }, - }, - layout: "padded", - }, -}; - -export const WithControls = () => { - 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 ( - - ); -}; - -export const WithNoControls = () => { - const data = { - groups: [], - controls: undefined, - resources: undefined, - }; - - return ( - {}} - onRequestOfResourcesData={() => {}} - /> - ); -}; - -export const WithError = () => { - const data = { - groups: "error", - controls: undefined, - resources: undefined, - }; - - return ( - {}} - onRequestOfResourcesData={() => {}} - /> - ); -}; - -export const WithoutRequiredPermissions = () => { - const data = { - groups: null, - controls: undefined, - resources: undefined, - }; - - return ( - {}} - onRequestOfResourcesData={() => {}} - /> - ); -}; - -export const Loading = () => { - const data = { - groups: undefined, - controls: undefined, - resources: undefined, - }; - - return ( - {}} - onRequestOfResourcesData={() => {}} - /> - ); -}; diff --git a/components/airview-compliance-ui/stories/control-overview/data.js b/components/airview-compliance-ui/stories/control-overview/data.js deleted file mode 100644 index 3b0966ca..00000000 --- a/components/airview-compliance-ui/stories/control-overview/data.js +++ /dev/null @@ -1,148 +0,0 @@ -import dayjs from "dayjs"; - -export const groups = [ - { - id: 1, - title: "Control group will resolve with controls", - }, - { - id: 2, - title: "Control group will resolve with no controls", - }, - { - id: 3, - title: "Control group will resolve with error", - }, -]; - -export const controls = { - 1: [ - { - id: 1, - name: "Control will resolve with resources", - severity: "Low", - applied: 5, - exempt: 6, - control: { - name: "Control label 1", - url: "/", - }, - frameworks: [ - { - name: "FWRK1", - url: "/", - }, - { - name: "FWRK2", - url: "/", - }, - ], - qualityModel: "Control type test label 1", - lifecycle: "Lifecycle test label 1", - }, - { - id: 2, - name: "Control will resolve with no resources", - severity: "Medium", - applied: 5, - exempt: 6, - control: { - name: "Control label 2", - url: "/", - }, - frameworks: [ - { - name: "FWRK1", - url: "/", - }, - { - name: "FWRK2", - url: "/", - }, - ], - qualityModel: "Control type test label 2", - lifecycle: "Lifecycle test label 2", - }, - { - id: 3, - name: "Control will resolve with error", - severity: "High", - applied: 5, - exempt: 6, - control: { - name: "Control label 3", - url: "/", - }, - frameworks: [ - { - name: "FWRK1", - url: "/", - }, - { - name: "FWRK2", - url: "/", - }, - ], - qualityModel: "Control type test label 3", - lifecycle: "Lifecycle test label 3", - }, - ], - 2: [], -}; - -export const resources = { - 1: [ - { - id: 1, - type: "Test instance 1", - reference: "Instance reference", - environment: "Production", - lastSeen: dayjs().toISOString(), - status: "Monitoring", - pending: false, - }, - { - id: 2, - type: "Test instance 2", - reference: "Instance reference", - environment: "Development", - lastSeen: dayjs().subtract(1, "day").toISOString(), - status: "Non-Compliant", - pending: false, - evidence: `Markdown Content`, - }, - { - id: 3, - type: "Test instance 3", - reference: "Instance reference", - environment: "Development", - lastSeen: dayjs().subtract(1, "month").toISOString(), - status: "Exempt", - pending: false, - exemptionData: { - ticket: "Ticket label for test instance 3", - expires: dayjs().toISOString(), - resources: ["Resource One", "Resource Two", "Resource Three"], - }, - }, - { - id: 4, - type: "Test instance 4", - reference: "Instance reference", - environment: "Development", - lastSeen: dayjs().subtract(1, "year").toISOString(), - status: "Exempt", - pending: true, - }, - { - id: 5, - type: "Test instance 5", - reference: "Instance reference", - environment: "Development", - lastSeen: dayjs().subtract(2, "year").toISOString(), - status: "Exempt", - pending: true, - }, - ], - 2: [], -}; diff --git a/components/airview-compliance-ui/stories/framework-view/framework-view-data.js b/components/airview-compliance-ui/stories/framework-view/framework-view-data.js deleted file mode 100644 index 3ccec1d5..00000000 --- a/components/airview-compliance-ui/stories/framework-view/framework-view-data.js +++ /dev/null @@ -1,90 +0,0 @@ -export const applicationsData = [ - { - id: 1, - controlID: "A&A-01", - controlTitle: "Audit and Assurance Policy and Procedures", - owner: "APP", - type: "Documentation", - applicationDetailData: { - detail: "Conduct audit according to the policy and procedures", - controlMappings: [ - { - name: "ISO27K", - gap: "partial", - }, - { - name: "NIST", - gap: "no", - }, - { - name: "CIS", - gap: "full", - }, - { - name: "PCI", - gap: "full", - }, - ], - Frequency: "12 Months", - }, - }, - { - id: 2, - controlID: "A&A-02", - controlTitle: "Independent Assessments", - owner: "APP", - type: "Manual", - applicationDetailData: { - detail: - "Conduct independent audit and assurance assessments according to relevant standards at least annually", - controlMappings: [ - { - name: "ISO27K", - gap: "partial", - }, - { - name: "NIST", - gap: "no", - }, - { - name: "CIS", - gap: "full", - }, - { - name: "PCI", - gap: "full", - }, - ], - Frequency: "12 Months", - }, - }, - { - id: 3, - controlID: "A&A-03", - controlTitle: "Risk Based Planning Assessment", - owner: "APP", - type: "Manual", - applicationDetailData: { - detail: "Conduct risk assessment", - controlMappings: [ - { - name: "ISO27K", - gap: "partial", - }, - { - name: "NIST", - gap: "no", - }, - { - name: "CIS", - gap: "full", - }, - { - name: "PCI", - gap: "full", - }, - ], - Frequency: "12 Months", - }, - }, -]; diff --git a/components/airview-compliance-ui/stories/framework-view/framework-view.stories.js b/components/airview-compliance-ui/stories/framework-view/framework-view.stories.js deleted file mode 100644 index 8fd1d963..00000000 --- a/components/airview-compliance-ui/stories/framework-view/framework-view.stories.js +++ /dev/null @@ -1,83 +0,0 @@ -import React from "react"; -import { FrameworkView } from "../../features/framework-view"; -import { applicationsData } from "./framework-view-data"; -import { Box } from "@mui/material"; - -export default { - title: "Features/Framework View", - component: FrameworkView, - parameters: { - layout: "padded", - }, - decorators: [ - (story) => { - return ( - - {story()} - - ); - }, - ], -}; - -function Template(args) { - return ; -} - -Template.args = { - title: "Audit & Assurance", - noDataMessage: { - title: "No Controls", - message: "There are no controls to display for this application", - }, - invalidPermissionsMessage: { - title: "Notice", - message: - "You do not have the required permissions to view the data for this application", - }, -}; - -export const Loading = { - ...Template, - args: { - ...Template.args, - loading: true, - applications: [], - }, -}; - -export const LoadedWithSingleControl = { - ...Template, - args: { - ...Template.args, - loading: false, - applications: [applicationsData[0]], - }, -}; - -export const LoadedWithMultipleControls = { - ...Template, - args: { - ...Template.args, - loading: false, - applications: applicationsData, - }, -}; - -export const LoadedWithNoControls = { - ...Template, - args: { - ...Template.args, - loading: false, - applications: [], - }, -}; - -export const WithoutRequiredPermissions = { - ...Template, - args: { - ...Template.args, - loading: false, - applications: null, - }, -}; diff --git a/components/airview-compliance-ui/stories/icon-chip/icon-chip.stories.js b/components/airview-compliance-ui/stories/icon-chip/icon-chip.stories.js deleted file mode 100644 index fa1d794a..00000000 --- a/components/airview-compliance-ui/stories/icon-chip/icon-chip.stories.js +++ /dev/null @@ -1,47 +0,0 @@ -import React from "react"; -import { IconChip } from "../../features/icon-chip"; -import WarningIcon from "@mui/icons-material/Warning"; - -export default { - title: "Features/IconChip", - component: IconChip, - argTypes: { - icon: { - control: false, - }, - color: { - control: { - type: "color", - }, - }, - labelColor: { - control: { - type: "color", - }, - }, - }, -}; - -function Template(args) { - return ; -} - -Template.args = { - icon: , - label: "Chip Label", - color: "#000", - labelColor: "#fff", - dense: false, -}; - -export const Default = { - ...Template, -}; - -export const Dense = { - ...Template, - args: { - ...Template.args, - dense: true, - }, -}; diff --git a/components/airview-compliance-ui/stories/progress-bar/progress-bar.stories.js b/components/airview-compliance-ui/stories/progress-bar/progress-bar.stories.js deleted file mode 100644 index b61c163a..00000000 --- a/components/airview-compliance-ui/stories/progress-bar/progress-bar.stories.js +++ /dev/null @@ -1,85 +0,0 @@ -import React from "react"; -import { ProgressBar } from "../../features/progress-bar"; -import { Box } from "@mui/material"; - -export default { - title: "Features/ProgressBar", - component: ProgressBar, - parameters: { - layout: "padded", - }, - argTypes: { - ariaLabel: { - control: false, - }, - classNames: { - control: false, - }, - color: { - control: { - type: "color", - }, - }, - value: { - control: { - type: "number", - min: 0, - max: 100, - step: 1, - }, - }, - }, - decorators: [ - (story) => { - const classes = { - root: { - width: "100%", - maxWidth: 1024, - margin: "0 auto", - flex: 1, - }, - }; - - return {story()}; - }, - ], -}; - -function Template(args) { - return ; -} - -export const Default = { - ...Template, - args: { - ...Template.args, - value: 33, - }, -}; - -export const WithoutLabel = { - ...Template, - args: { - ...Template.args, - value: 33, - showLabel: false, - }, -}; - -export const Determinate = { - ...Template, - args: { - ...Template.args, - value: 33, - variant: "determinate", - }, -}; - -export const Indeterminate = { - ...Template, - args: { - ...Template.args, - variant: "indeterminate", - showLabel: false, - }, -}; diff --git a/components/airview-ui/aside-and-main/aside-and-main.js b/components/airview-ui/aside-and-main/aside-and-main.js deleted file mode 100644 index d63d22e7..00000000 --- a/components/airview-ui/aside-and-main/aside-and-main.js +++ /dev/null @@ -1,42 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { Container as MuiContainer, Box } from "@mui/material"; - -export function AsideAndMainContainer({ children }) { - return ( - - {children} - - ); -} - -AsideAndMainContainer.propTypes = { - children: PropTypes.node, -}; - -export function Main({ children, sx }) { - return ( - - {children} - - ); -} - -Main.propTypes = { - children: PropTypes.node, -}; - -export function Aside({ children, sx }) { - return ( - - {children} - - ); -} - -Aside.propTypes = { - children: PropTypes.node, -}; diff --git a/components/airview-ui/aside-and-main/index.js b/components/airview-ui/aside-and-main/index.js deleted file mode 100644 index c683b94b..00000000 --- a/components/airview-ui/aside-and-main/index.js +++ /dev/null @@ -1 +0,0 @@ -export { AsideAndMainContainer, Aside, Main } from "./aside-and-main"; diff --git a/components/airview-ui/index.js b/components/airview-ui/index.js deleted file mode 100644 index 8758a753..00000000 --- a/components/airview-ui/index.js +++ /dev/null @@ -1,7 +0,0 @@ -// export * from "./airview-ui-theme-provider"; -export * from "./top-bar"; -export * from "./navigation-drawer"; -export * from "./menu"; -export * from "./page-title"; -export * from "./aside-and-main"; -export * from "./styled-wysiwyg"; diff --git a/components/airview-ui/is-link-url-internal.js b/components/airview-ui/is-link-url-internal.js deleted file mode 100644 index e15ec672..00000000 --- a/components/airview-ui/is-link-url-internal.js +++ /dev/null @@ -1,3 +0,0 @@ -export function isLinkInternal(url) { - return /^\/(?!\/)/.test(url); -} diff --git a/components/airview-ui/menu/buttonmenu.js b/components/airview-ui/menu/buttonmenu.js deleted file mode 100644 index 2ce7e31d..00000000 --- a/components/airview-ui/menu/buttonmenu.js +++ /dev/null @@ -1,162 +0,0 @@ -import React, { useState } from "react"; -import PropTypes from "prop-types"; -import { Box, Collapse, IconButton, Typography, Skeleton, ButtonBase } from "@mui/material"; -import NextLinkComposed from 'next/link'; -import KeyboardArrowRightIcon from "@mui/icons-material/KeyboardArrowRight"; -import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; -import { isLinkInternal } from "../is-link-url-internal"; - -export function ButtonMenu({ - menuTitle, - menuTitleElement = "h3", - loading = false, - fetching = false, - menuItems, - collapsible = true, - initialCollapsed = true, - handleButtonClick, - currentRoute, - sx, - ...rest -}) { - const [collapsed, setCollapsed] = useState( - collapsible ? initialCollapsed : false - ); - - return ( - - - - {loading ? : menuTitle} - - - {collapsible && ( - setCollapsed((prevState) => !prevState)} - size="medium" - aria-label={collapsed ? "Expand menu" : "Collapse menu"} - disabled={loading} - sx={{ - marginLeft: 1, - padding: 0, - color: "primary.main", - }} - > - {collapsed ? ( - - ) : ( - - )} - - )} - - - - {menuItems?.map(({ groupTitle, links }, index) => ( - - {groupTitle && ( - - {loading ? : groupTitle} - - )} - li": { - fontSize: 14, - marginBottom: 1, - color: "text.secondary", - }, - }} - > - {loading - ? [...Array(6)].map((item, index) => ( - - )) - : links?.map(({ label, url }, index) => { - return ( - - handleButtonClick(url, label)} - sx={{ - textDecoration: "none", - textTransform: 'none', - textAlign: 'left', - fontWeight: 'light', - color: 'secondary.main', - ...(url === currentRoute && { fontWeight: "bold" }), - }} - > - {label} - - - ); - })} - - - ))} - - - ); -} - -ButtonMenu.propTypes = { - menuTitle: PropTypes.string.isRequired, - menuTitleElement: PropTypes.string, - loading: PropTypes.bool, - fetching: PropTypes.bool, - menuItems: PropTypes.arrayOf( - PropTypes.shape({ - groupTitle: PropTypes.string, - links: PropTypes.arrayOf( - PropTypes.shape({ - label: PropTypes.string.isRequired, - url: PropTypes.string.isRequired, - }) - ).isRequired, - }) - ).isRequired, - collapsible: PropTypes.bool, - initialCollapsed: PropTypes.bool, - handleButtonClick: PropTypes.func.isRequired, - currentRoute: PropTypes.string, - sx: PropTypes.object, -}; diff --git a/components/airview-ui/menu/index.js b/components/airview-ui/menu/index.js deleted file mode 100644 index 127d63b0..00000000 --- a/components/airview-ui/menu/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export { Menu } from "./menu"; -export { ButtonMenu } from "./buttonmenu"; diff --git a/components/airview-ui/menu/menu.js b/components/airview-ui/menu/menu.js deleted file mode 100644 index 787863be..00000000 --- a/components/airview-ui/menu/menu.js +++ /dev/null @@ -1,168 +0,0 @@ -import React, { useState } from "react"; -import PropTypes from "prop-types"; -import { - Box, - Collapse, - IconButton, - Typography, - Skeleton, - Link, -} from "@mui/material"; -import NextLink from 'next/link'; -// import NextLinkComposed from 'next/link' -import KeyboardArrowRightIcon from "@mui/icons-material/KeyboardArrowRight"; -import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; -import { isLinkInternal } from "../is-link-url-internal"; - -export function Menu({ - menuTitle, - menuTitleElement = "h3", - loading = false, - fetching = false, - menuItems, - collapsible = true, - initialCollapsed = true, - linkComponent, - currentRoute, - sx, - ...rest -}) { - const [collapsed, setCollapsed] = useState( - collapsible ? initialCollapsed : false - ); - - return ( - - - - {loading ? : menuTitle} - - - {collapsible && ( - setCollapsed((prevState) => !prevState)} - size="medium" - aria-label={collapsed ? "Expand menu" : "Collapse menu"} - disabled={loading} - sx={{ - marginLeft: 1, - padding: 0, - color: "primary.main", - }} - > - {collapsed ? ( - - ) : ( - - )} - - )} - - - - {menuItems?.map(({ groupTitle, links }, index) => ( - - {groupTitle && ( - - {loading ? : groupTitle} - - )} - li": { - fontSize: 14, - marginBottom: 1, - color: "text.secondary", - }, - }} - > - {loading - ? [...Array(6)].map((item, index) => ( - - )) - : links?.map(({ label, url }, index) => { - return ( - - - {label} - - - ); - })} - - - ))} - - - ); -} - -Menu.propTypes = { - menuTitle: PropTypes.string.isRequired, - menuTitleElement: PropTypes.string, - loading: PropTypes.bool, - fetching: PropTypes.bool, - menuItems: PropTypes.arrayOf( - PropTypes.shape({ - groupTitle: PropTypes.string, - links: PropTypes.arrayOf( - PropTypes.shape({ - label: PropTypes.string.isRequired, - url: PropTypes.string.isRequired, - }) - ).isRequired, - }) - ).isRequired, - collapsible: PropTypes.bool, - initialCollapsed: PropTypes.bool, - linkComponent: PropTypes.any, - currentRoute: PropTypes.string, - sx: PropTypes.object, -}; diff --git a/components/airview-ui/navigation-drawer/index.js b/components/airview-ui/navigation-drawer/index.js deleted file mode 100644 index 9c8429b7..00000000 --- a/components/airview-ui/navigation-drawer/index.js +++ /dev/null @@ -1 +0,0 @@ -export { NavigationDrawer } from "./navigation-drawer"; diff --git a/components/airview-ui/navigation-drawer/navigation-drawer.js b/components/airview-ui/navigation-drawer/navigation-drawer.js deleted file mode 100644 index 999fb7fe..00000000 --- a/components/airview-ui/navigation-drawer/navigation-drawer.js +++ /dev/null @@ -1,44 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { Box } from "@mui/material"; - -export function NavigationDrawer({ - open, - top = 0, - children, - drawerWidth = 300, -}) { - return ( - - {children} - - ); -} - -NavigationDrawer.propTypes = { - open: PropTypes.bool.isRequired, - top: PropTypes.number, - children: PropTypes.node, - drawerWidth: PropTypes.number, -}; diff --git a/components/airview-ui/page-title/index.js b/components/airview-ui/page-title/index.js deleted file mode 100644 index c76ae2d4..00000000 --- a/components/airview-ui/page-title/index.js +++ /dev/null @@ -1 +0,0 @@ -export { PageTitle } from "./page-title"; diff --git a/components/airview-ui/page-title/page-title.js b/components/airview-ui/page-title/page-title.js deleted file mode 100644 index a4dd85cc..00000000 --- a/components/airview-ui/page-title/page-title.js +++ /dev/null @@ -1,37 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { Typography, Skeleton } from "@mui/material"; - -export function PageTitle({ - title, - loading = false, - fetching = false, - component, - sx, - ...rest -}) { - return ( - - {loading ? : title} - - ); -} - -PageTitle.propTypes = { - title: PropTypes.string.isRequired, - loading: PropTypes.bool, - fetching: PropTypes.bool, - component: PropTypes.string, - sx: PropTypes.object, -}; diff --git a/components/airview-ui/styled-wysiwyg/index.js b/components/airview-ui/styled-wysiwyg/index.js deleted file mode 100644 index 56909af0..00000000 --- a/components/airview-ui/styled-wysiwyg/index.js +++ /dev/null @@ -1 +0,0 @@ -export { StyledWysiwyg } from "./styled-wysiwyg"; diff --git a/components/airview-ui/styled-wysiwyg/styled-wysiwyg-loading.js b/components/airview-ui/styled-wysiwyg/styled-wysiwyg-loading.js deleted file mode 100644 index 7e54a444..00000000 --- a/components/airview-ui/styled-wysiwyg/styled-wysiwyg-loading.js +++ /dev/null @@ -1,24 +0,0 @@ -import React from "react"; -import { Skeleton, Typography } from "@mui/material"; - -export function StyledWysiwygLoading() { - return ( -
-
- {[...Array(5)].map((item, index) => ( - - . - - ))} -
- -
- {[...Array(7)].map((item, index) => ( - - . - - ))} -
-
- ); -} diff --git a/components/airview-ui/styled-wysiwyg/styled-wysiwyg.js b/components/airview-ui/styled-wysiwyg/styled-wysiwyg.js deleted file mode 100644 index 68893fb9..00000000 --- a/components/airview-ui/styled-wysiwyg/styled-wysiwyg.js +++ /dev/null @@ -1,195 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { Box } from "@mui/material"; -import { red } from "@mui/material/colors"; -import { StyledWysiwygLoading } from "./styled-wysiwyg-loading"; - -export function StyledWysiwyg({ - children, - sx, - loading = false, - fetching = false, - ...rest -}) { - if (loading) return ; - - return ( - ({ - ...theme.typography.h1, - fontSize: 34, - }), - "& h2": (theme) => ({ - ...theme.typography.h2, - fontSize: 28, - }), - "& h3": (theme) => ({ - ...theme.typography.h3, - fontSize: 24, - }), - "& h4": (theme) => ({ - ...theme.typography.h4, - fontSize: 22, - }), - "& h5": (theme) => ({ - ...theme.typography.h5, - fontSize: 20, - }), - "& h6": (theme) => ({ - ...theme.typography.h6, - fontSize: 18, - }), - "& h1, h2, h3, h4, h5, h6": { - fontWeight: "fontWeightMedium", - marginY: 2, - }, - "& hr": { - border: 0, - height: 0, - borderBottom: 1, - borderColor: "divider", - marginY: 4, - display: "block", - }, - "& img": { - marginY: 2, - display: "block", - }, - "& a, a:visited": { - color: "primary.main", - }, - "& a:hover, a:focus": { - textDecoration: "none", - }, - "& code": { - fontSize: "90%", - fontFamily: "Consolas,'Liberation Mono',Courier,monospace", - color: red[900], - borderRadius: "4px", - backgroundColor: "grey.100", - padding: "3px 6px", - }, - "& ul, & ol": { - listStylePosition: "outside", - padding: "0 0 0 36px", - }, - "& blockquote": { - paddingLeft: 2, - borderLeft: 4, - borderColor: "primary.main", - marginX: 0, - marginY: 2, - - "&:before": { - content: "none", - }, - - "& cite": { - display: "block", - fontStyle: "normal", - fontWeight: "fontWeightMedium", - fontSize: 14, - }, - }, - "& pre": { - display: "block", - width: "100%", - whiteSpace: "pre", - overflow: "auto", - backgroundColor: "grey.900", - borderRadius: 1, - tabSize: 2, - wordWrap: "initial", - fontSize: 14, - marginX: 0, - marginY: 2, - padding: 0, - - "& code": { - overflowX: "auto", - display: "block", - padding: 2, - backgroundColor: "transparent", - border: 0, - color: "grey.400", - fontSize: "100%", - }, - }, - "& table": (theme) => ({ - borderCollapse: "collapse", - width: "100%", - textAlign: "left", - marginX: 0, - marginY: 2, - fontFamily: "default", - fontWeight: "regular", - - // Scrollable table for smaller viewports - [theme.breakpoints.down(800)]: { - overflow: "hidden", - overflowX: "scroll", - display: "block", - whiteSpace: "nowrap", - height: "auto", - overflowScrolling: "touch", - WebkitOverflowScrolling: "touch", - }, - - "& th": { - fontWeight: "medium", - }, - - "& td": { - verticalAlign: "top", - }, - - "& tbody": { - wordBreak: "break-word", - }, - - "& th, & td": { - border: 1, - borderColor: "grey.300", - minWidth: 100, - }, - - "& thead th, & thead td, & tbody th, & tbody td, & tfoot th, & tfoot td": - { - padding: 1, - }, - - "& thead, & tbody tr:nth-of-type(even)": { - backgroundColor: "grey.100", - }, - - "& p": { - fontSize: "inherit", - }, - - "& th p": { - fontWeight: "inherit", - }, - }), - }, - ...(fetching && { opacity: 0.5, pointerEvents: "none" }), - ...sx, - }} - {...rest} - > - {children} - - ); -} - -StyledWysiwyg.propTypes = { - children: PropTypes.node, - sx: PropTypes.object, - loading: PropTypes.bool, - fetching: PropTypes.bool, -}; diff --git a/components/airview-ui/top-bar/index.js b/components/airview-ui/top-bar/index.js deleted file mode 100644 index a7ad81e4..00000000 --- a/components/airview-ui/top-bar/index.js +++ /dev/null @@ -1 +0,0 @@ -export { TopBar } from "./top-bar"; diff --git a/components/airview-ui/top-bar/top-bar.js b/components/airview-ui/top-bar/top-bar.js deleted file mode 100644 index 487393c3..00000000 --- a/components/airview-ui/top-bar/top-bar.js +++ /dev/null @@ -1,49 +0,0 @@ -import React from "react"; -import PropTypes from "prop-types"; -import { AppBar, Box, Toolbar, Typography, IconButton } from "@mui/material"; -import MenuIcon from "@mui/icons-material/Menu"; -import CloseIcon from "@mui/icons-material/Close"; - -export function TopBar({ - onNavButtonClick, - title, - color, - position, - top = 0, - navOpen, -}) { - return ( - - - - - {navOpen ? : } - - - {title} - - - - - ); -} - -TopBar.propTypes = { - onNavButtonClick: PropTypes.func.isRequired, - title: PropTypes.string.isRequired, - color: PropTypes.string, - position: PropTypes.string, - top: PropTypes.number, - navOpen: PropTypes.bool.isRequired, -}; diff --git a/components/compliance/ControlData.jsx b/components/compliance/ControlData.jsx deleted file mode 100644 index ee985ecd..00000000 --- a/components/compliance/ControlData.jsx +++ /dev/null @@ -1,197 +0,0 @@ -import { Box, Typography, Table, TableContainer, TableHead, TableRow, TableCell, TableBody, Card, CardContent, Chip, Stepper, Step, StepLabel, Button, Alert, TabList, Tab, Tabs } from '@mui/material'; -import { useState } from 'react'; - -function FrameworkPanel({value, index, framework}) { - return ( - - ); -} - -function MethodPanel({value, index, method}) { - // const { children, value, index, ...other } = props; - - return ( - - ); -} - - -function MethodsList({ methods }) { - const [value, setValue] = useState(0); - const handleChange = (event, newValue) => { - setValue(newValue); - }; - - return ( - - - Technical or Audit/Manual methods in place to enforce the control - - - - - {methods.map((method, index) => ( - - ))} - - - {methods.map((method, index) => ( - - ))} - - - ); -} - - -function FrameworksList({ frameworks }) { - const [value, setValue] = useState(0); - const handleChange = (event, newValue) => { - setValue(newValue); - }; - - return ( - - - Industry or Organisational Frameworks that the control maps to. - - - - - {frameworks.map((framework, index) => ( - - ))} - - - {frameworks.map((framework, index) => ( - - ))} - - - ); -} - - - -export function ControlDataDisplay({ data }) { - - // // console.log('ControlDataDisplay:data: ', data) - const steps = ['Overview', 'Frameworks', 'Methods']; - const [activeStep, setActiveStep] = useState(0); - - const handleStep = (step) => () => { - setActiveStep(step); - }; - - data = data.data - return ( - - - - - {steps.map((label, index) => ( - - - - ))} - - - {activeStep === 0 && ( - - {data?.description || ''} - {data?.discussion || ''} - - Provider: - - - Service: {data?.service || 'N/A'} - - - Control Owner: - - - Quality Model: - - - Control Owner: - - - - - )} - {activeStep === 1 && ( - - )} - {activeStep === 2 && ( - - )} - - - ); -} \ No newline at end of file diff --git a/components/compliance/ControlFrameworkTables.jsx b/components/compliance/ControlFrameworkTables.jsx deleted file mode 100644 index 47f72154..00000000 --- a/components/compliance/ControlFrameworkTables.jsx +++ /dev/null @@ -1,316 +0,0 @@ -import Table from '@mui/material/Table'; -import TableBody from '@mui/material/TableBody'; -import TableCell from '@mui/material/TableCell'; -import TableContainer from '@mui/material/TableContainer'; -import TableHead from '@mui/material/TableHead'; -import TableRow from '@mui/material/TableRow'; -import Paper from '@mui/material/Paper'; -import { Stack } from '@mui/material' -import { Chip } from '@mui/material' -import * as React from 'react'; - -// import { DataGrid } from '@mui/x-data-grid'; - -// import { Table, TableHead, TableBody, TableRow, TableCell } from '@mui/material'; - - - - -import { useState } from 'react'; -import { DataGrid } from '@mui/x-data-grid'; -import { IconButton, Button, Dialog, DialogTitle, DialogContent, styled } from '@mui/material'; -import { KeyboardArrowDown, KeyboardArrowUp } from '@mui/icons-material'; -import { Info, Close } from '@mui/icons-material'; -import { createTheme, ThemeProvider } from '@mui/material'; -import { baseTheme } from '../../constants/baseTheme'; - -const theme = createTheme({ - components: { - MuiDataGrid: { - styleOverrides: { - root: { - '& .MuiDataGrid-columnHeaders': { - backgroundColor: baseTheme.palette.background.primary, - color: 'white', - }, - }, - }, - }, - }, -}); - - -export const FrameworkCoverageTable = ({ rows, sx }) => { - const [selectedRow, setSelectedRow] = useState(null); - const [open, setOpen] = useState(false); - - const handleRowClick = (row) => { - setSelectedRow(row); - setOpen(true); - }; - - const handleDialogClose = () => { - setOpen(false); - }; - - const renderInfoButton = (params) => { - return ( - handleRowClick(params.row)} - size="small" - > - - - ); - }; - - const columns = [ - { field: 'appName', headerName: 'Application Name', flex: 1 }, - { field: 'businessUnit', headerName: 'Business Unit', flex: 1 }, - { field: 'exemptions', headerName: 'Exemptions', flex: 1 }, - { - field: '', - headerName: ' ', - width: 50, - align: 'right', - renderCell: (params) => ( - <> - {params.value} - {renderInfoButton(params)} - - ), - }, - ]; - - const rowsWithId = rows.map((row) => ({ ...row, id: row.id.toString() })); - - return ( - - - - {selectedRow && ( - <> - -
- {selectedRow.appName} Controls - -
-
- - - - - Control Name - Issues - Criticality - - - - {selectedRow.controls.map((control) => ( - - {control.name} - {control.issues} - {control.criticality} - - ))} - -
-
- - )} -
-
- ); -}; - - - -// export const FrameworkCoverageTable = ({ rows, sx }) => { -// const [expandedRows, setExpandedRows] = useState([]); - -// const handleRowExpand = (params) => { -// const expandedRowIds = new Set(expandedRows); -// if (expandedRowIds.has(params.id)) { -// expandedRowIds.delete(params.id); -// } else { -// expandedRowIds.add(params.id); -// } -// setExpandedRows(Array.from(expandedRowIds)); -// }; - -// const renderCollapsibleElement = (row) => { -// return ( -//
-// -// -// -// Control Name -// Issues -// Criticality -// -// -// -// {row.controls.map((control) => ( -// -// {control.name} -// {control.issues} -// {control.criticality} -// -// ))} -// -//
-//
-// ); -// }; - -// const columns = [ -// { field: 'appName', headerName: 'Application Name', flex: 1 }, -// { field: 'businessUnit', headerName: 'Business Unit', flex: 1 }, -// { field: 'exemptions', headerName: 'Exemptions', flex: 1 }, -// ]; - -// const rowsWithCollapsible = rows.map((row) => ({ -// ...row, -// isExpanded: expandedRows.includes(row.id), -// })); - -// return ( -// true} -// renderRowDetail={(params) => renderCollapsibleElement(params.row)} -// onRowExpand={handleRowExpand} -// sx={{...sx}} - -// /> -// ); -// }; - -// export default CollapsibleTable; - -const columns = [ - { field: 'id', headerName: 'APP ID', width: 70 }, - { field: 'appName', headerName: 'Application', width: 130 }, - { field: 'businessUnit', headerName: 'Business Unit', width: 130 }, - { - field: 'exemptions', - headerName: 'Exemptions', - type: 'number', - width: 90, - } -]; - - -// const columns = [ -// { field: 'id', headerName: 'APP ID', width: 70 }, -// { field: 'appName', headerName: 'Application', width: 130 }, -// { field: 'businessUnit', headerName: 'Business Unit', width: 130 }, -// { -// field: 'age', -// headerName: 'Age', -// type: 'number', -// width: 90, -// }, -// { -// field: 'fullName', -// headerName: 'Full name', -// description: 'This column has a value getter and is not sortable.', -// sortable: false, -// width: 160, -// valueGetter: (params) => -// `${params.row.firstName || ''} ${params.row.lastName || ''}`, -// }, -// ]; - - -const rows = [ - { id: 1, appName: 'Airview', businessUnit: 'Cloud CoE', exemptions: 35, controls: [{ name: 'AC1', name: 'Control Storage Accounts', issues: 5, criticality: 'high' }, { name: 'AC2', name: 'Control Storage Accounts', issues: 5, criticality: 'high' }, { name: 'AC3', name: 'Control Storage Accounts', issues: 5, criticality: 'high' }] }, - { id: 2, appName: 'Microsoft Teams', businessUnit: 'Central IT', exemptions: 42, controls: [{ name: 'AC1', name: 'Control Storage Accounts', issues: 5, criticality: 'high' }, { name: 'AC2', name: 'Control Storage Accounts', issues: 5, criticality: 'high' }, { name: 'AC3', name: 'Control Storage Accounts', issues: 5, criticality: 'high' }] }, - { id: 3, appName: 'Public Website', businessUnit: 'Marketing', exemptions: 45, controls: [{ name: 'AC1', name: 'Control Storage Accounts', issues: 5, criticality: 'high' }, { name: 'AC2', name: 'Control Storage Accounts', issues: 5, criticality: 'high' }, { name: 'AC3', name: 'Control Storage Accounts', issues: 5, criticality: 'high' }] }, -]; - - -controls: [{ name: 'AC1', name: 'Control Storage Accounts', issues: 5, criticality: 'high' }, { name: 'AC2', name: 'Control Storage Accounts', issues: 5, criticality: 'high' }, { name: 'AC3', name: 'Control Storage Accounts', issues: 5, criticality: 'high' }] - -export function FrameworkCoverageTableOld({ sx }) { - return ( - - ); -} - -function Implementation({ methods }) { - if (methods) { - return ( - - - {methods.map((row, i) => ( - row.system && - ))} - - - ) - - } else { - return ( - - ) - } -} -export function ControlTable({ controls, framework = 'NIST80053' }) { - - // // console.log('ControlTable:controls: ', controls) - - return ( - - - - - Framework Domain - Control - Description - Implementation(s) - Owner - - - - {controls.map((row, i) => ( - - - {/* {row.data?.frameworks?.find(f => f.name === framework)?.mapping?.section ?? null} */} - {row.data?.frameworks?.filter(f => f.name === framework).flatMap(f => f.mapping?.map(m => m.domain)).join(", ") || null} - - - {row?.data?.id ?? null} - {row?.data?.description ?? null} - - {row?.data?.control_owner ?? null} - - ))} - -
-
- ); -} \ No newline at end of file diff --git a/components/compliance/ControlTable.jsx b/components/compliance/ControlTable.jsx deleted file mode 100644 index ec7595a3..00000000 --- a/components/compliance/ControlTable.jsx +++ /dev/null @@ -1,64 +0,0 @@ -import Table from '@mui/material/Table'; -import TableBody from '@mui/material/TableBody'; -import TableCell from '@mui/material/TableCell'; -import TableContainer from '@mui/material/TableContainer'; -import TableHead from '@mui/material/TableHead'; -import TableRow from '@mui/material/TableRow'; -import Paper from '@mui/material/Paper'; -import { Stack } from '@mui/material' -import { Chip } from '@mui/material' - - -function Implementation({ methods }) { - if (methods) { - return ( - - - {methods.map((row, i) => ( - row.system && - ))} - - - ) - - } else { - return ( - - ) - } -} -export function ControlTable({ controls, framework = 'NIST80053' }) { - - // // console.log('ControlTable:controls: ', controls) - - return ( - - - - - Framework Domain - Control - Description - Implementation(s) - Owner - - - - {controls.map((row, i) => ( - - - {/* {row.data?.frameworks?.find(f => f.name === framework)?.mapping?.section ?? null} */} - {row.data?.frameworks?.filter(f => f.name === framework).flatMap(f => f.mapping?.map(m => m.domain)).join(", ") || null} - - - {row?.data?.id ?? null} - {row?.data?.description ?? null} - - {row?.data?.control_owner ?? null} - - ))} - -
-
- ); -} \ No newline at end of file diff --git a/components/content/ContentPage.jsx b/components/content/ContentPage.jsx deleted file mode 100644 index 0349dac3..00000000 --- a/components/content/ContentPage.jsx +++ /dev/null @@ -1,568 +0,0 @@ -import React, { useEffect, useState, useCallback } from 'react' - -import { baseTheme } from '../../constants/baseTheme'; -import { MDXProvider } from "@mdx-js/react"; -import { mdComponents } from "../../constants/mdxProvider.js"; -import { Editor } from '@/components/editor' - -import { ThemeProvider } from '@mui/material/styles'; -import CssBaseline from '@mui/material/CssBaseline'; -import Link from '@mui/material/Link'; -import { TopBar, ControlBar } from '@/components/dashboard'; -import { Menu, NavigationDrawer, ButtonMenu } from '@/components/airview-ui'; -import { PagedOutput } from '@/components/display/PagedOutput'; -import { PresentationOutput } from '@/components/display/PresentationOutput'; -import SlideshowIcon from '@mui/icons-material/Slideshow'; -import EditNoteIcon from '@mui/icons-material/EditNote'; -import { IconButton, Typography, MenuItem, Box, Alert, Grid, ButtonBase, LinearProgress, Skeleton, Paper } from '@mui/material'; -import { FullScreenSpinner } from "@/components/dashboard/index.js"; - -import { AsideAndMainContainer, Aside, Main } from '@/components/airview-ui'; -import { TableOfContents } from '@/components/content'; -import { ContentWrapperContext } from '@/components/content'; -import { Etherpad } from '@/components/etherpad'; -import { useSelector, useDispatch } from 'react-redux' -import store from '@/lib/redux/store' -import { setBranch } from '@/lib/redux/reducers/branchSlice' - -import { useRouter } from 'next/router' - -import deepEqual from 'deep-equal'; -import path from 'path'; - -export function ContentPage({ - pageContent, - file, - content, - menuStructure, - handleContentChange, - handlePageReset, - collection, - context, - menuComponent, - headerComponent = null, - sideComponent = null, - isLoading -}) { - - const [frontmatter, setFrontmatter] = useState(pageContent.frontmatter); - const MenuComponent = menuComponent; - const SideComponent = sideComponent; - - const isEmptyObject = (obj) => { - return Object.keys(obj).length === 0; - }; - - // Use useCallback to memoize the frontMatterCallback function - const frontMatterCallback = useCallback((newFrontmatter) => { - if (!isEmptyObject(newFrontmatter) && !deepEqual(frontmatter, newFrontmatter)) { - setFrontmatter(newFrontmatter); - } - }, [frontmatter]); // Make sure to include frontmatter in the dependency array - - - // ControlBar - const [controlBarOpen, setControlBarOpen] = useState(false); - - const queryBranch = useRouter()?.query?.branch ?? null; // this loads direct links to the content using ?branch=whatever query parameter - const currentState = store.getState(); - const reduxBranch = currentState.branch.name; - if (queryBranch != reduxBranch) { - console.log('queryBranch: ', queryBranch, ' : ', reduxBranch) - // const dispatch = useDispatch() - // dispatch(setBranch(queryBranch)) - // setControlBarOpen(true) - // setChangeBranch(true) - } // set the branch from the query parameter ?branch= - - - - const handleEditMode = (mode) => { - setEditMode(mode) - setMenuOpen(!mode) - } - - const navDrawerWidth = 300; - const topBarHeight = controlBarOpen ? 64 + 64 : 64; - const [menuOpen, setMenuOpen] = useState(true); - const [print, setPrint] = useState(false); - const [presentation, setPresentation] = useState(false); - const [editMode, setEditMode] = useState(false); ////-----------------------------true for testing edit mode - - - - const handleOnNavButtonClick = () => setMenuOpen((prevState) => !prevState); - - const { primary, relatedContent } = menuStructure || {}; - - function handleRefresh() { - // console.log('ContentPage:handleRefresh:context: ', context) - handleContentChange(context.file) - } - - function handlePrint() { - setPrint(!print); - setMenuOpen(print); - }; - - function handlePresentation() { - setPresentation(!presentation); - }; - - - // let Content =

tesr

; - // let Content = FullScreenSpinner ; - - const Content = () => { - if (context && context.file && context.file.endsWith('.etherpad')) { - return - } else if (context && pageContent.content && pageContent.frontmatter) { - const Page = pageContent.content; - return ; - } else { - return - } - } - - useEffect(() => { // update the frontmatter - if (pageContent.frontmatter) { - setFrontmatter(pageContent.frontmatter); - } - }, [pageContent.frontmatter]); - - - if (isLoading) { - return - } - - - if (!print && !presentation && !editMode) { - return ( - - - - setControlBarOpen(controlBarOpen => !controlBarOpen)} - /> - - - - - {menuStructure && } -
- - - - {/*
*/} -
- - - - <> - -
- - -
-
-
- ) - } else if (print) { - return ( - - - - - - <> - - - - - ) - } else if (presentation) { - return ( - - - - <> - - - - ) - } else if (editMode) { - return ( - - - - setControlBarOpen(controlBarOpen => !controlBarOpen)} - /> - -
- - -
-
-
- ) - - } -} - -function Banner({ frontmatter, handlePresentation, headerComponent, editMode }) { - const [environment, setEnvironment] = useState(""); - const HeaderComponent = headerComponent; - - useEffect(() => { - const fetchData = async () => { - const resp = await fetch("/api/environment"); - const data = await resp.json(); - console.log(data) - setEnvironment(data) - } - fetchData() - }, []); - - - return ( - <> - { - headerComponent ? ( - - ) : ( - {frontmatter?.title && frontmatter.title} - ) - } - {frontmatter?.format === 'presentation' && !editMode && - - This is a presentation. View in presentation mode by clicking - - - - - - - - - } - - {frontmatter?.padID && - - This is draft content from Etherpad. edit here: - - - - - - - - - } - - ) - - -} - - - - -function ContentMenu({ content, file, handleContentChange, handlePageReset, context, loading = false }) { - // let directory = file?.includes("/") ? file.split("/")[1] : file; - - let directory = file ? path.dirname(file) : null; - - let chaptersMenu = [] - if (content && content[directory]) { - for (let collectionItem of context.collections) { - if (content[directory][collectionItem]) { - // console.log('ContentMenu:collectionItem: ', collectionItem) - chaptersMenu.push( - { - groupTitle: collectionItem, - links: content[directory][collectionItem] - } - ) - } - } - - if (content[directory].chapters) { - chaptersMenu.push( - { - groupTitle: "Chapters", - links: content[directory].chapters - } - ) - } - // if (content[directory].knowledge) { - // chaptersMenu.push( - // { - // groupTitle: "Knowledge", - // links: content[directory].knowledge - - // } - // ) - // } - // if (content[directory].designs) { - // chaptersMenu.push( - // { - // groupTitle: "Designs", - // links: content[directory].designs - // } - // ) - // } - } - if (chaptersMenu) { - // return (null) - - return ( - <> - handlePageReset()} - sx={{ - textDecoration: "none", - textTransform: 'none', - textAlign: 'left', - fontWeight: 'bold', - color: 'secondary.main', - mb: '5%' - }} - >Main Content - - - - ) - // return ( - // - // ); - } -} - - - - -function SolutionsMenu({ solutions, open, top, drawerWidth }) { - // // console.log('SolutionsMenu: ', solutions) - - return ( - - {solutions && solutions.map((c, i) => - {c.label})} - - ); -} - - -function LeftMenu({ menu, open, top, drawerWidth }) { - - // console.log('LeftMenu:menu: ', menu) - - return ( - - {menu && - menu.length > 0 && - menu.map((c) => ( - - -

{c.label}

- - {c.children && } -
- ))} - -
- ); -} - -const L2Menu = ({ menu }) => { - return ( - <> - {menu && Object.entries(menu).map(([key, children]) => ( -
- - - {/* -

- {key} -

- {children && children.map((item, index) => ( - - - {item.label} - - ))} */} -
- ))} - - ); -}; - -// {menu.map((c, i) => -// {c.label})} - - - - -function BasicLeftMenu({ menu, open, top, drawerWidth }) { - // console.log('BasicLeftMenu: ', menu) - - return ( - - {menu && menu.map((c, i) => - {c.label})} - - ); -} - - - -const capitalizeFirstLetter = (string) => { - return string.charAt(0).toUpperCase() + string.slice(1); -}; - - - - -function ContentSkeleton({ topBarHeight }) { - - return ( - - - - - -
- - - -
- -
-
-
-
) - -} - - - -function LinearIndeterminate() { - return ( - - - - ); -} \ No newline at end of file diff --git a/components/content/ContentView.jsx b/components/content/ContentView.jsx deleted file mode 100644 index 61f4d2c7..00000000 --- a/components/content/ContentView.jsx +++ /dev/null @@ -1,367 +0,0 @@ -import React, { useState } from 'react' - -import { baseTheme } from '../../constants/baseTheme'; - -import { ThemeProvider } from '@mui/material/styles'; -import CssBaseline from '@mui/material/CssBaseline'; -import Link from '@mui/material/Link'; -import { TopBar } from '@/components/dashboard'; -import { Menu, NavigationDrawer, ButtonMenu } from '@/components/airview-ui'; -import { PagedOutput } from '@/components/display/PagedOutput'; -import { PresentationOutput } from '@/components/display/PresentationOutput'; -import SlideshowIcon from '@mui/icons-material/Slideshow'; -import { IconButton, Typography, MenuItem, Box, Alert, Grid, ButtonBase, LinearProgress, Skeleton } from '@mui/material'; - -import { AsideAndMainContainer, Aside, Main } from '@/components/airview-ui'; - -export function ContentView({ - children, - frontmatter, - file, - content, - menuStructure, - pageStructure, - handleContentClick, - siteConfig = null, - isLoading = true, - isError = false -}) { - - console.log('loading: ', isLoading) - - const navDrawerWidth = 300; - const topBarHeight = 65; - if (!isLoading) { - return - } - - // console.log('SolutionView:menuStructure: ', menuStructure) - - const [menuOpen, setMenuOpen] = useState(true); - const [print, setPrint] = useState(false); - const [presentation, setPresentation] = useState(false); - - const handleOnNavButtonClick = () => setMenuOpen((prevState) => !prevState); - - const { primary, relatedContent } = pageStructure || {}; - // console.log('SolutionView:pageStructure: ', pageStructure) - function handlePrint() { - setPrint(!print); - setMenuOpen(print); - }; - - function handlePresentation() { - setPresentation(!presentation); - }; - - if (!print && !presentation) { - return ( - - - - - -
- {frontmatter?.title && frontmatter.title} - {frontmatter?.format === 'presentation' && - - This is a presentation. View in presentation mode by clicking - - - - - - - - - } - - {frontmatter?.padID && - - This is draft content from Etherpad edit here: - - - - - - - - - } - - -
- {children && children} -
- -
-
-
- ) - } else if (print) { - return ( - - - - {children && children} - - - ) - } else if (presentation) { - return ( - - {children && children} - - ) - } -} - - -function AsideMenu({ content, file, handleContentClick, siteConfig }) { - let directory = file?.includes("/") ? file.split("/")[1] : file; - // // console.log('ChaptersMenu:File ', file) - let chaptersMenu = [] - if (content && content[directory]) { - - for (let collection in siteConfig.content) { - if (content[directory][collection]) { - chaptersMenu.push( - { - groupTitle: capitalizeFirstLetter(collection), - links: content[directory][collection] - } - ) - } - } - - - if (content[directory].chapters) { - chaptersMenu.push( - { - groupTitle: "Chapters", - links: content[directory].chapters - } - ) - } - - } - if (chaptersMenu) { - return ( - <> - handleContentClick('/' + file, 'primary link')} - sx={{ - textDecoration: "none", - textTransform: 'none', - textAlign: 'left', - fontWeight: 'bold', - color: 'secondary.main', - mb: '5%' - }} - >Main Content - - - ) - - } -} - - - - -function SolutionsMenu({ solutions, open, top, drawerWidth }) { - // // console.log('SolutionsMenu: ', solutions) - - return ( - - {solutions && solutions.map((c, i) => - {c.label})} - - ); -} - - - -function ContentMenu({ menu, open, top, drawerWidth }) { - - // console.log('ContentMenu:menu: ', menu ) - - return ( - - {menu && - menu.length > 0 && - menu.map((c) => ( - - -

{c.label}

- - {c.children && } -
- ))} - -
- ); -} - -const L2Menu = ({ menu }) => { - return ( - <> - {menu && Object.entries(menu).map(([key, children]) => ( -
- - - {/* -

- {key} -

- {children && children.map((item, index) => ( - - - {item.label} - - ))} */} -
- ))} - - ); -}; - -// {menu.map((c, i) => -// {c.label})} - - -const capitalizeFirstLetter = (string) => { - return string.charAt(0).toUpperCase() + string.slice(1); -}; - - - -function ContentSkeleton ({topBarHeight}) { - - return ( - - - - - -
- - - {frontmatter?.title && frontmatter.title} - - - - - - -
- {null} -
- -
-
-
) - -} \ No newline at end of file diff --git a/components/content/ContentWrapperContext.jsx b/components/content/ContentWrapperContext.jsx deleted file mode 100644 index 8c35f47d..00000000 --- a/components/content/ContentWrapperContext.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import React, { createContext, useState, useContext } from 'react'; - -// Create a context -const GitContext = createContext(); - -// Create a provider component -export const ContentWrapperContext = ({ children, defaultBranch }) => { - const [currentBranch, setCurrentBranch] = useState(defaultBranch); - - return ( - - {children} - - ); -}; - -export function useBranch() { - const context = useContext(GitContext); - - if (!context) { - throw new Error('useBranch must be used within a ContentWrapperContext'); - } - - return context; - } \ No newline at end of file diff --git a/components/content/IndexView.jsx b/components/content/IndexView.jsx deleted file mode 100644 index 828468a0..00000000 --- a/components/content/IndexView.jsx +++ /dev/null @@ -1,85 +0,0 @@ -import React, { useState } from 'react' -import { Box, Typography, LinearProgress } from '@mui/material'; -import { baseTheme } from '../../constants/baseTheme'; -import { ThemeProvider } from '@mui/material/styles'; -import CssBaseline from '@mui/material/CssBaseline'; -import Grid from '@mui/material/Grid'; -import Container from '@mui/material/Container'; -import { Tile } from '@/components/dashboard/Tiles' -import { TopBar } from '@/components/dashboard'; -import path from 'path'; - -import { siteConfig } from "../../site.config.js"; - -export function IndexView({ - tiles, - menuStructure, - title, - menuComponent, - loading -}) { - - // console.log('IndexView:menuStructure: ', menuStructure) - - const MenuComponent = menuComponent; - - const navDrawerWidth = 300; - const topBarHeight = 65; - const [menuOpen, setMenuOpen] = useState(true); - - const handleOnNavButtonClick = () => setMenuOpen((prevState) => !prevState); - return ( - - - - - -
- {title} - - - {tiles ? ( - tiles.map((c, i) => ( - - ))) : ( - - )} - - - - -
-
- ) -} - - -function LinearIndeterminate() { - return ( - - - - ); -} \ No newline at end of file diff --git a/components/content/TableOfContents.jsx b/components/content/TableOfContents.jsx deleted file mode 100644 index 571c864f..00000000 --- a/components/content/TableOfContents.jsx +++ /dev/null @@ -1,110 +0,0 @@ -import React, {useState} from 'react'; -import { Link as MuiLink, Box, Typography, List, ListItem, ListItemText, styled, Collapse, IconButton } from '@mui/material'; -import KeyboardArrowRightIcon from "@mui/icons-material/KeyboardArrowRight"; -import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; -// Custom styled components -const SmallListItemText = styled(ListItemText)(({ theme }) => ({ - fontSize: theme.typography.body2.fontSize, - lineHeight: theme.typography.body2.lineHeight, - paddingBottom: theme.spacing(0), - paddingTop: theme.spacing(0), - marginBottom: theme.spacing(0), - marginTop: theme.spacing(0), -})); - -const generateTableOfContents = (tableOfContents, numbering = []) => { - if (!tableOfContents || tableOfContents.length === 0) { - return null; - } - - return ( - - {tableOfContents.map((item, index) => { - const { depth, value, id, children } = item; - const itemNumbering = [...numbering, index + 1]; - const numberingString = itemNumbering.join('.'); - - const listItemText = ( - - {`${numberingString} `} - {value} - - ); - - if (children && children.length > 0) { - return ( - - {listItemText} - - {generateTableOfContents(children, itemNumbering)} - - - ); - } - - return ( - - - {listItemText} - - - ); - })} - - ); -}; - -export const TableOfContents = ({ tableOfContents }) => { - const [collapsed, setCollapsed] = useState(false); - const menuTitleElement = "h3" - const loading = false; - const menuTitle = 'Page Contents' - return ( - - - - {loading ? : menuTitle} - - - setCollapsed((prevState) => !prevState)} - size="medium" - aria-label={collapsed ? "Expand menu" : "Collapse menu"} - disabled={loading} - sx={{ - marginLeft: 1, - padding: 0, - color: "primary.main", - }} - > - {collapsed ? ( - - ) : ( - - )} - - - - - - {generateTableOfContents(tableOfContents)} - - - - ); -}; diff --git a/components/content/index.js b/components/content/index.js deleted file mode 100644 index ca09b8b0..00000000 --- a/components/content/index.js +++ /dev/null @@ -1,5 +0,0 @@ -export { IndexView } from './IndexView' -export { ContentView } from './ContentView' -export { ContentPage } from './ContentPage' -export { TableOfContents } from './TableOfContents' -export { ContentWrapperContext, useBranch } from './ContentWrapperContext' \ No newline at end of file diff --git a/components/controls/ControlsView.jsx b/components/controls/ControlsView.jsx deleted file mode 100644 index 21c2dce4..00000000 --- a/components/controls/ControlsView.jsx +++ /dev/null @@ -1,98 +0,0 @@ - -export function ControlsView({ - menu, // the menu from staticProps - content, // will be a page or nested layout - frontmatter = null, // frontmatter collected from the page and the mdx file - context = null, // the context from the page to help with relative files and links - pageData = null, // controls for the menu - controls = null - }) { - - const { navItems, csp } = createMenu(menu); - - - - let navItemsControls = null; - // // console.log('ControlView:context', context); - if (controls) { navItemsControls = createControlMenu(controls) } else { - navItemsControls = [ - { - groupTitle: "Controls", - links: [] - }]; - } - // // console.log('navItemsControls :', navItemsControls) - const navItemsDocs = [ - { - groupTitle: "Infrastructure-as-Code", - links: [ - { - label: "terraform-azure-storage", - url: "", - }, - ], - }, - { - groupTitle: "Designs", - links: [ - { - label: "Static Content Website", - url: "", - }, - { - label: "Data Lakes", - url: "", - }, - ], - }, - ]; - const navDrawerWidth = 300; - const topBarHeight = 64; - const [menuOpen, setMenuOpen] = useState(true); - - const handleOnNavButtonClick = () => setMenuOpen((prevState) => !prevState); - return ( - - - - - - - -
- - Control Overview - obj.file === context.router.asPath)} /> - -
- - ) - } - \ No newline at end of file diff --git a/components/controls/index.js b/components/controls/index.js deleted file mode 100644 index e0bbe183..00000000 --- a/components/controls/index.js +++ /dev/null @@ -1 +0,0 @@ -export { ControlsView } from './ControlsView' \ No newline at end of file diff --git a/components/dashboard/Cards/Stats.js b/components/dashboard/Cards/Stats.js deleted file mode 100644 index 61c6ad90..00000000 --- a/components/dashboard/Cards/Stats.js +++ /dev/null @@ -1,107 +0,0 @@ -// @mui material components -import Card from "@mui/material/Card"; -import Grid from "@mui/material/Grid"; -import Icon from "@mui/material/Icon"; - -import Box from "@mui/material/Box"; -import Typography from "@mui/material/Typography"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; - -export function MiniStatisticsCard({ - bgColor, - color = "primary", - title, - count, - percentage = null, - icon, - direction = "left", -}) { - // const [controller] = useArgonController(); - // const { darkMode } = controller; - const darkMode = false; - const iconcolor = icon.color + ".main"; - - return ( - - - {direction === "left" ? ( - - - {/* */} - - {/* */} - - - ) : null} - - - {title} - - {count} - - {percentage.value && ( - - {percentage?.value} - - )} - - {percentage?.text} - - - - {direction === "right" ? ( - - - {/* */} - - {/* */} - - - ) : null} - - - ); -} diff --git a/components/dashboard/Headers/ServicesHeader.js b/components/dashboard/Headers/ServicesHeader.js deleted file mode 100644 index aa58dc53..00000000 --- a/components/dashboard/Headers/ServicesHeader.js +++ /dev/null @@ -1,94 +0,0 @@ -import React from 'react' - -import Grid from '@mui/material/Grid'; - -import { Stack } from '@mui/material' -import { Chip } from '@mui/material' -import { MiniStatisticsCard } from "@/components/dashboard"; -import { Dialog, DialogTitle, DialogContent, DialogActions, Button, IconButton, Typography, Box } from '@mui/material'; -import { Container as MuiContainer } from "@mui/material"; - - -export function ServicesHeader({ frontmatter, extraData }) { - - const controlCoverage = createControlCoverage(extraData); - - if (!frontmatter) { return <> } - // frontmatter = frontmatter.frontmatter - // console.log('ServicesHeader:controlCoverage: ', controlCoverage) - // // console.log('ServicesHeader:frontmatter: ', frontmatter) - - let icon = { color: 'success', icon: 'check' } - - if (controlCoverage && controlCoverage.controlCoverage < 50) { - icon = { color: 'error', icon: 'circle-exclamation' } - } else if (controlCoverage && controlCoverage.controlCoverage < 100) { - icon = { color: 'warning', icon: 'triangle-exclamation' } - } else if (!controlCoverage.controlCoverage) { - icon = { color: 'info', icon: 'circle-exclamation' } - } - - return ( - - {/* {children} */} - - {/* */} - - - {frontmatter.title} - - {frontmatter.status === 'approved' ? : } - {(frontmatter?.resilience?.redundancy) ? : } - {frontmatter?.resilience?.find(item => item.name === "availability") ? ( - item.name === "availability").availability}`} - color="success" - /> - ) : ( - - )} - - - - - - - - {/* */} - - ) -} - - - - - -function createControlCoverage(controls) { - // // console.log('createControlCoverage:controls: ', controls) - - let controlCountCovered = 0 - let controlCountUnCovered = 0 - let controlMethods = 0 - let controlCoverage = 0 - - - for (const control of controls) { - if (control.data && control.data.methods && control.data.methods.length > 0) { - controlMethods += control.data.methods.length - controlCountCovered++ - } else { - controlCountUnCovered++ - } - } - // calculate the percentage of covered controls vs controls - controlCoverage = Math.round((controlCountCovered / controls.length) * 100) - // // console.log('createControlCoverage:controlCoverage: ', controlCoverage) - return ({ controlCountCovered, controlCountUnCovered, controlMethods, controlCoverage, controlCount: controls.length }) -}; - diff --git a/components/dashboard/Headers/index.js b/components/dashboard/Headers/index.js deleted file mode 100644 index eaad88e9..00000000 --- a/components/dashboard/Headers/index.js +++ /dev/null @@ -1 +0,0 @@ -export { ServicesHeader } from './ServicesHeader' \ No newline at end of file diff --git a/components/dashboard/Loader/index.js b/components/dashboard/Loader/index.js deleted file mode 100644 index e5cbce22..00000000 --- a/components/dashboard/Loader/index.js +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react'; -import { styled } from '@mui/material/styles'; -import CircularProgress from '@mui/material/CircularProgress'; - -const FullScreenSpinnerWrapper = styled('div')({ - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - height: '400px', - width: '100%', - // position: 'fixed', - // top: 0, - // left: 0, - zIndex: 9999, - // backgroundColor: 'rgba(0, 0, 0, 0.5)', -}); - -export const FullScreenSpinner = () => { - return ( - - - - ); -}; - diff --git a/components/dashboard/Menus/ControlsMenu.js b/components/dashboard/Menus/ControlsMenu.js deleted file mode 100644 index a2496782..00000000 --- a/components/dashboard/Menus/ControlsMenu.js +++ /dev/null @@ -1,88 +0,0 @@ -import React, { useState } from 'react' -import { ButtonMenu } from '@/components/airview-ui'; -import { ControlDataDisplay } from '@/components/compliance/ControlData'; -import CloseIcon from '@mui/icons-material/Close'; - -import { Dialog, DialogTitle, DialogContent, DialogActions, Button, IconButton, Typography, Box } from '@mui/material'; - - -export function ControlsMenu({ - controls -}) { - - const [dialogOpen, setDialogOpen] = useState(false); - const [controlUrl, setControlUrl] = useState(''); - - const handleControlClick = (url, label) => { - // Show the dialog box - setDialogOpen(true); - const selectedControl = controls.find((control) => control.file === url); - setControlUrl({ url, label, selectedControl }); - - }; - - return ( - <> - - - {controlUrl.selectedControl && - setDialogOpen(false)} fullWidth={true} maxWidth={'lg'} sx={{ - "& .MuiDialog-container": { - alignItems: "flex-start" - } - }} - scroll='paper'> - - {controlUrl.selectedControl?.data?.friendly_name || controlUrl.selectedControl?.data?.name } ({controlUrl.selectedControl?.data?.id || 'N/A'}) - setDialogOpen(false)} aria-label="close"> - - - - - - - - } - - ) - -} - - - -function createControlMenu(controls) { - // console.log('createControlMenu:controls: ', controls) - try { - const links = controls.map((control) => { - const label = control.data.friendly_name || control.data.id || ''; // Adjust the property name according to your control data structure - const url = control.file; - - return { - label, - url, - }; - }); - - return [ - { - links: links - }]; - } catch (error) { - // // console.log('createControlMenu:error: ', error) - - return [ - { - groupTitle: "Controls", - links: [], - }] - } -}; - - diff --git a/components/dashboard/Menus/FullHeaderMenu.js b/components/dashboard/Menus/FullHeaderMenu.js deleted file mode 100644 index d0a3be67..00000000 --- a/components/dashboard/Menus/FullHeaderMenu.js +++ /dev/null @@ -1,130 +0,0 @@ -import React from 'react' -import { NavigationDrawer, Menu } from '@/components/airview-ui'; -// import Link from '@mui/material/Link'; -import Link from 'next/link' - - -function convertToObjectArray(inputObject) { - const result = []; - - for (const groupTitle in inputObject) { - if (inputObject.hasOwnProperty(groupTitle)) { - const links = inputObject[groupTitle].map(item => ({ - label: item.label, - url: item.url, - })); - - result.push({ groupTitle, links }); - } - } - - return result; -} - -export function FullHeaderMenu({ menu, open, top, drawerWidth }) { - - // console.log('FullHeaderMenu:menu: ', menu) - - // [{ links: x.children }] - - // { - // groupTitle: "Menu Group Title One", - // links: [ - // { - // label: "Menu Item One", - // url: "", - // }, - // { - // label: "Menu Item Two", - // url: "", - // }, - // ], - // }, - - let menuItems = [{ links: null}] - - - return ( - - {menu && - menu.length > 0 && - menu.map((x, i) => - - )} - - ); - - - - return ( - - {menu && - menu.length > 0 && - menu.map((c) => ( - - -

{c.label}

- - {c.children && } -
- ))} - -
- ); -} - - -const L2Menu = ({ menu }) => { - return ( - <> - {menu && Object.entries(menu).map(([key, children]) => ( -
- - - {/* -

- {key} -

- {children && children.map((item, index) => ( - - - {item.label} - - ))} */} -
- ))} - - ); -}; - -// {menu.map((c, i) => -// {c.label})} - - -const capitalizeFirstLetter = (string) => { - return string.charAt(0).toUpperCase() + string.slice(1); -}; \ No newline at end of file diff --git a/components/dashboard/Menus/HeaderMinimalMenu.js b/components/dashboard/Menus/HeaderMinimalMenu.js deleted file mode 100644 index 30c0b1a1..00000000 --- a/components/dashboard/Menus/HeaderMinimalMenu.js +++ /dev/null @@ -1,69 +0,0 @@ -import React from 'react' -import { NavigationDrawer, Menu } from '@/components/airview-ui'; -import Link from '@mui/material/Link'; - - -export function HeaderMinimalMenu({ menu, open, top, drawerWidth }) { - - // console.log('HeaderMinimalMenu:menu: ', menu) - - return ( - - {menu && - menu.length > 0 && - menu.map((c) => ( - - -

{c.label}

- - {c.children && } -
- ))} - -
- ); -} - - -const L2Menu = ({ menu }) => { - return ( - <> - {menu && Object.entries(menu).map(([key, children]) => ( -
- - - {/* -

- {key} -

- {children && children.map((item, index) => ( - - - {item.label} - - ))} */} -
- ))} - - ); -}; - -// {menu.map((c, i) => -// {c.label})} - - -const capitalizeFirstLetter = (string) => { - return string.charAt(0).toUpperCase() + string.slice(1); -}; \ No newline at end of file diff --git a/components/dashboard/Menus/ListMenu.js b/components/dashboard/Menus/ListMenu.js deleted file mode 100644 index e034c540..00000000 --- a/components/dashboard/Menus/ListMenu.js +++ /dev/null @@ -1,34 +0,0 @@ -import React from 'react' -import { MenuItem, Skeleton } from '@mui/material'; -import { NavigationDrawer } from '@/components/airview-ui'; -import Link from '@mui/material/Link'; - - -export function ListMenu({ menu, open, top, drawerWidth }) { - return ( - - {menu ? - menu.map((c, i) => ( - - {c.label} - - )) - : - - } - - ); -}; - -const MenuSkeleton = () => ( - <> - {[...Array(10)].map((_, index) => ( - - - labsdsdsddsdsdsdsdsdss sdsdsds sdsdsdel - - - ))} - -); - diff --git a/components/dashboard/Menus/index.js b/components/dashboard/Menus/index.js deleted file mode 100644 index fa2ee095..00000000 --- a/components/dashboard/Menus/index.js +++ /dev/null @@ -1,4 +0,0 @@ -export { HeaderMinimalMenu } from './HeaderMinimalMenu'; -export { ListMenu } from './ListMenu'; -export { FullHeaderMenu } from './FullHeaderMenu'; -export { ControlsMenu } from './ControlsMenu'; \ No newline at end of file diff --git a/components/dashboard/Tiles/index.js b/components/dashboard/Tiles/index.js deleted file mode 100644 index a8b7319b..00000000 --- a/components/dashboard/Tiles/index.js +++ /dev/null @@ -1,85 +0,0 @@ -import { Box, Typography, Stack, Chip, Link, Grid } from '@mui/material'; - -export function Tile({ name, url, image }) { - return ( - - - - {image && ( - - )} - - - {name} - - - - - - ); -} - - -export function ServiceTile({ frontmatter, file }) { - - return ( - - - - {frontmatter.title} - - - {frontmatter.description} - - - {frontmatter.status === 'approved' ? : } - - - - - ) -} \ No newline at end of file diff --git a/components/dashboard/TopBar.jsx b/components/dashboard/TopBar.jsx deleted file mode 100644 index d330afe6..00000000 --- a/components/dashboard/TopBar.jsx +++ /dev/null @@ -1,142 +0,0 @@ -import React, { useState } from "react"; -import AppBar from '@mui/material/AppBar'; -import Button from '@mui/material/Button'; -import IconButton from '@mui/material/IconButton' -import Menu from '@mui/material/Menu'; -import MenuItem from '@mui/material/MenuItem'; -import { Menu as MenuIcon, ExpandMore as ExpandMoreIcon } from '@mui/icons-material'; -import ArrowBackIosNewOutlinedIcon from '@mui/icons-material/ArrowBackIosNewOutlined'; -import Toolbar from '@mui/material/Toolbar'; -import Typography from '@mui/material/Typography'; -import Container from '@mui/material/Container'; -import Link from '@mui/material/Link'; -import CloseIcon from "@mui/icons-material/Close"; -import PrintIcon from '@mui/icons-material/Print'; -import SlideshowIcon from '@mui/icons-material/Slideshow'; -import { styled } from "@mui/material/styles"; -import { siteConfig } from "../../site.config.js"; -import MoreIcon from '@mui/icons-material/MoreVert'; - -// import logo from '../../public/logos/airwalk-logo.png'; -const Logo = styled("img")({ - display: "block", - width: "auto", - height: 64, - }); - -export function TopBar({ - onNavButtonClick, navOpen, menu=false, back=false, topBarHeight=64, logo=true, handlePrint, handlePresentation, handleMore }) { - - const [anchorEl, setAnchorEl] = useState(null); - const [activeMenu, setActiveMenu] = useState(''); - - const handleMenuOpen = (event, id) => { - setAnchorEl(event.currentTarget); - setActiveMenu(id); - }; - - const handleMenuClose = () => { - setAnchorEl(null); - setActiveMenu(''); - }; - - const handleMoreClick = () => { - if (typeof handleMore === 'function') { - handleMore(); - } else { - console.error('TopBar: Error: handleMore is not a function'); - } - }; - - - - return ( - - - {/* has menu */} - {menu && {navOpen ? : }} - {logo && } - {/* back button */} - {back && } - - - - - - {(siteConfig.content.applications) && } - {(siteConfig.content.customers) && } - - - - - Etherpads - - - - - {(siteConfig.content.providers || siteConfig.content.services) && - - Providers & Services - - } - {(siteConfig.content.frameworks) && - - Frameworks & Standards - - } - {(siteConfig.content.solutions) && - - Solutions - - } - {(siteConfig.content.products) && - - Products - - } - - - - - Business Applications - - - - - Business Units - - - - - handleMoreClick()} - color="inherit" - > - - - - - - - ); - }; - diff --git a/components/dashboard/TopBar/ControlBar.jsx b/components/dashboard/TopBar/ControlBar.jsx deleted file mode 100644 index d44ad7ad..00000000 --- a/components/dashboard/TopBar/ControlBar.jsx +++ /dev/null @@ -1,151 +0,0 @@ -import React, { useState } from "react"; - -import { Toolbar, AppBar, FormControlLabel, Switch, IconButton, TextField, Stack, Autocomplete } from '@mui/material'; -import PrintIcon from '@mui/icons-material/Print'; -import SlideshowIcon from '@mui/icons-material/Slideshow'; -import { useSelector, useDispatch } from 'react-redux' -import { setBranch } from '@/lib/redux/reducers/branchSlice' - - - -export function ControlBar({ - open, height, handleEdit, handleRefresh, handlePrint, handlePresentation, collection: initialCollection }) { - const [edit, setEdit] = useState(false); - const [collection, setCollection] = useState(initialCollection); - - const [changeBranch, setChangeBranch] = useState(false); - - // const queryBranch = useRouter()?.query?.branch ?? null; // this loads direct links to the content using ?branch=whatever query parameter - const dispatch = useDispatch() - - const [branches, setBranches] = useState([{ name: 'main' }]); - - const handleBranchClick = async (open = 'ignore') => { // handles the toggling of the "Change Branch" selector - // console.log('handleBranchClick:changeBranch: ', changeBranch) - if (changeBranch) { - await dispatch(setBranch(collection)) // default the branch back - // console.log('handleBranchClick:reset: ', collection.branch) - handleRefresh(); // reset the page - } else { - fetchBranches(collection); - } - setChangeBranch(!changeBranch); - if (open == 'open') { setChangeBranch(true) } else if (open == 'close') { setChangeBranch(false) }; - }; - - function fetchBranches(collection) { - const branches = async () => { - const res = await fetch(`/api/repo/get-branches?owner=${collection.owner}&repo=${collection.repo}`); // fetch draft content to add to the menus. - const data = await res.json(); - setBranches(data) - }; - branches() - } - - async function handleBranch(event, value) { // handles the branch selector changing - if (value) { - - const newCollection = {...collection, branch: value } - console.log('ControlBar:handleBranch:collection: ', collection) - - console.log('ControlBar:handleBranch:newCollection: ', newCollection) - // await dispatch(setBranch(newCollection)) - await dispatch(setBranch(newCollection)) - - handleRefresh(); // reset the page - } - // setSelectedBranch(value); - } - - const handlePresentationClick = () => { - if (typeof handlePresentation === 'function') { - handlePresentation(); - } else { - console.error('TopBar: Error: handlePresentation is not a function'); - } - }; - const handlePrintClick = () => { - if (typeof handlePrint === 'function') { - handlePrint(); - } else { - console.error('TopBar: Error: handlePrint is not a function'); - } - }; - - const handleEditClick = () => { - // localStorage.setItem('editMode', JSON.stringify(editMode)); - if (typeof handleEdit === 'function') { - handleEdit(!edit); - if (edit) {handleBranchClick('close')} else {handleBranchClick('open')}; - setEdit(!edit) - - } else { - console.error('TopBar: Error: handleEdit is not a function'); - } - }; - - return ( - - -
- handleEditClick()} /> - - - } label="Edit Mode" /> - - handleBranchClick()} /> - - - } label="Change Branch" /> - {(changeBranch || edit ) && collection && } label="" />} -
-
- {handlePrint && handlePrintClick()} - color="primary" - > - - } label="Print" />} - {handlePresentation && handlePresentationClick()} - color="primary" - > - - } label="View Presentation" />} -
-
-
- ) - -} - -function BranchSelector({ defaultBranch, handleBranch, branches }) { - const { name: reduxBranch } = useSelector((state) => state.branch); - let branch; - - if (reduxBranch?.name !== 'none') { - branch = defaultBranch - } else { - branch = reduxBranch - } - - return ( - - - option.name)} - renderInput={(params) => } - /> - - ) - -} \ No newline at end of file diff --git a/components/dashboard/index.js b/components/dashboard/index.js deleted file mode 100644 index 86da8f39..00000000 --- a/components/dashboard/index.js +++ /dev/null @@ -1,5 +0,0 @@ -export { MiniStatisticsCard } from "./Cards/Stats"; -export { TopBar } from "./TopBar"; -export { FullScreenSpinner } from "./Loader"; - -export { ControlBar } from './TopBar/ControlBar' diff --git a/components/display/MDXViewer.jsx b/components/display/MDXViewer.jsx deleted file mode 100644 index ab3dc886..00000000 --- a/components/display/MDXViewer.jsx +++ /dev/null @@ -1,133 +0,0 @@ -import React from "react"; -import GlobalStyles from '@mui/material/GlobalStyles'; -import PresentationMode from "@/components/presentations/PresentationMode"; -import Swipeable from "@/components/presentations/Swipeable"; -import { Storage } from "../../hooks/useStorage"; - -import dynamic from 'next/dynamic' - -import Zoom from '@/components/presentations/Zoom'; -import Box from '@mui/material/Box'; - - -const globalStyles = ` - - body, - html { - overflow: auto; - width: 100vw; - margin: 0; - padding: 0; - background-color: grey; - } - - @print { - @top-left { - background: #4AC7F0; - content: ''; - display: block; - height: .05cm; - opacity: .5; - width: 100%; } - @top-center { - background: #4AC7F0; - content: ''; - display: block; - height: .05cm; - opacity: .5; - width: 100%; } - @top-right { - content: string(heading); - font-size: 9pt; - height: 1cm; - vertical-align: middle; - width: 100%; } - @bottom-center { - background: #4AC7F0; - content: ''; - display: block; - height: .05cm; - opacity: .5; - width: 100%; } - @bottom-right { - background: #4AC7F0; - content: counter(page); - height: 1cm; - text-align: center; - width: 1cm; } - } - - @page :blank { - @top-left { - background: none; - content: ''; } - @top-center { - content: none; } - @top-right { - content: none; } } - - @page no-chapter { - /* @top-left { - background: none; - content: none; } - @top-center { - content: none; } */ - @top-right { - content: none; } } - - @print :first { - background: url(../../../../backgrounds/airwalk-header.png) no-repeat; - /* background-image: url(/backgrounds/airwalk-header.png) no-repeat; */ - background-size: cover; - margin: 0; } - - @print { - /* background: url(../../../../airwalk/Airwalk-Logo-Blue.png) no-repeat; */ - background: url(/airwalk/Airwalk-Logo-Blue.png) no-repeat; - background-position: left -10px bottom -50px; - background-size: 140px; - padding-top: 20px; - padding-bottom: 15px; - z-index: -1000 ; - size: A4; - } - - @page chapter { - background: #002b3d; - margin: 0; - /* @top-left { - content: none; } - @top-center { - content: none; } */ - @top-right { - content: none; } } - - #slide { - display: flex; - overflow: hidden; - -webkit-overflow-scrolling: touch; - } - -`; - -export default dynamic(() => Promise.resolve(MDXViewer), { - ssr: false, -}); - -function MDXViewer({ children, next }) { - - // const pageSize = { width:1920, height:1080} - const pageSize = { width:2480, height:3508} - - // 2480 x 3508 A4 - - return ( - - - - - {children} - - - ); -} diff --git a/components/display/PagedOutput.jsx b/components/display/PagedOutput.jsx deleted file mode 100644 index 34854774..00000000 --- a/components/display/PagedOutput.jsx +++ /dev/null @@ -1,111 +0,0 @@ -import React, { useState, useEffect, useRef, useLayoutEffect } from 'react' -import { Box, useScrollTrigger, Fab, Fade } from '@mui/material'; -import { Previewer } from 'pagedjs' -import { FullScreenSpinner } from "@/components/dashboard/index.js"; - -import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp'; -import CloseIcon from '@mui/icons-material/Close'; - - -export function PagedOutput({ children, handlePrint }) { - const mdxContainer = useRef(null); - const previewContainer = useRef(null); - const [rendered, setRendered] = useState(false); - - useEffect(() => { - // if (!rendered && children) { - - const timerId = setTimeout(() => { // wait for a bit! - const paged = new Previewer(); - const contentMdx = `${mdxContainer.current.innerHTML}`; - - // Clear the content of previewContainer - previewContainer.current.innerHTML = ''; - paged.preview(contentMdx, ['/pdf.css'], previewContainer.current).then((flow) => { - setRendered(true); - - // Delay the removal of the second instance of .pagedjs_pages - // setTimeout(() => { - // const pagedPages = previewContainer.current.getElementsByClassName('pagedjs_pages'); - // if (pagedPages.length > 1) { - // pagedPages[0].remove(); - // } - // }, 0); - }); - - }, 5000); - - // Clean up the timer to avoid memory leaks - return () => clearTimeout(timerId); - - - // } - }, [children]); - - - return ( - <> - handlePrint()} - role="presentation" - sx={{ position: 'fixed', top: 16, right: 16, displayPrint: 'none' }} - > - - - - - -
- - -
-
- {children && children} -
- - - - - - - ) - - -} - -function ScrollTop({ children }) { - // const { children, window } = props; - // Note that you normally won't need to set the window ref as useScrollTrigger - // will default to window. - // This is only being set here because the demo is in an iframe. - const trigger = useScrollTrigger({ - // target: window ? window() : undefined, - disableHysteresis: true, - threshold: 100, - }); - - const handleClick = (event) => { - const anchor = (event.target.ownerDocument || document).querySelector( - '#back-to-top-anchor', - ); - - if (anchor) { - anchor.scrollIntoView({ - block: 'center', - }); - } - }; - - return ( - - - {children} - - - ); -} diff --git a/components/display/PresentationOutput.jsx b/components/display/PresentationOutput.jsx deleted file mode 100644 index ad36eeec..00000000 --- a/components/display/PresentationOutput.jsx +++ /dev/null @@ -1,75 +0,0 @@ -import React, { useState, useEffect } from 'react' -import { Box, Fab } from '@mui/material'; -import { ThemeProvider } from '@mui/material/styles'; -import CssBaseline from '@mui/material/CssBaseline'; -import CloseIcon from '@mui/icons-material/Close'; -import PrintIcon from '@mui/icons-material/Print'; -import dynamic from 'next/dynamic' -import { FullScreenSpinner } from '@/components/dashboard/index.js'; -import { useMDX } from '@/lib/content/mdx' -import { theme } from '../../constants/theme'; -import { CurrentSlideProvider } from "../../context/CurrentSlideContext"; -import { ModeProvider } from "../../context/ModeContext"; - -export const PresentationOutput = dynamic(() => Promise.resolve(PresentationOutputPage), { - ssr: true, -}); - - -function PresentationOutputPage({ children, handlePresentation, refresh = false, content }) { - const [pageContent, setContent] = useState({ content: undefined, frontmatter: undefined }); - const [ printView , setPrintView ] = useState(false); - const [ viewType, setViewType ] = useState('SlidePage') - - const handlePrint = () => { - if (!printView) { - setViewType('PrintSlide') - } else { - setViewType('SlidePage') - } - setPrintView(!printView) - -}; - - - useEffect(() => { - const { mdxContent, frontmatter } = useMDX(content, 'mdx', viewType); - setContent({ content: mdxContent, frontmatter: frontmatter }); - }, [content, viewType]) - - if (pageContent.content && pageContent.frontmatter) { - const Content = pageContent.content; - return ( - <> - handlePresentation()} role="presentation" sx={{ position: 'fixed', top: 16, right: 16, displayPrint: 'none', zIndex: 9999 }}> - - - - - handlePrint()} role="presentation" sx={{ position: 'fixed', top: 16, right: 80, displayPrint: 'none', zIndex: 9999 }}> - - - - - - {/*
*/} - - - - - - - - - {/*
*/} -
- - ) - } else { - - return ( - - ) - } - -} diff --git a/components/display/PrintSlide.jsx b/components/display/PrintSlide.jsx deleted file mode 100644 index 94bf0cc8..00000000 --- a/components/display/PrintSlide.jsx +++ /dev/null @@ -1,112 +0,0 @@ -import React, { useEffect } from "react"; -// import { useRouter } from "next/router"; -import GlobalStyles from '@mui/material/GlobalStyles'; -import { Slide } from 'airview-mdx'; -// import PresentationMode from "@/components/presentations/PresentationMode"; -// import Swipeable from "@/components/presentations/Swipeable"; -// import useEventListener from "../hooks/useEventListener"; -// import { useTotalPages } from "../context/TotalPagesContext"; -// import { useMode } from "../context/ModeContext"; -// import { useCurrentSlide } from "../context/CurrentSlideContext"; -// import { Storage } from "../hooks/useStorage"; -// import { MODES } from "../constants/modes"; - -import dynamic from 'next/dynamic' - -import Zoom from '@/components/presentations/Zoom'; -import { Box } from '@mui/material'; - - -const globalStyless = ` - - body, - html { - overflow: auto; - width: 100vw; - margin: 0; - padding: 0; - background-color: black; - } - - @media print { - @page { - margin: 0mm 0mm 0mm 0mm; - size: 1280px 1080px; - } - } - - #slide { - display: flex; - overflow: hidden; - -webkit-overflow-scrolling: touch; - } - -`; - - -const globalStyles = ` - - body - { - overflow: auto; - width: 100vw; - height: 100vh; - margin: 0; - padding: 0; - background-color: black; - } -`; - -export default dynamic(() => Promise.resolve(PrintSlide), { - ssr: false, -}); - -function PrintSlide({ children, next }) { - // // // console.log('PrintSlide:children : ', children) - let generatedSlides = []; - let generatorCount = 0; - // Filter down children by only Slides - React.Children.map(children, (child) => { - // Check for
element to separate slides - const childType = child && child.props && (child.type || []); - if (childType && childType === "hr") { - generatorCount += 1; - return; - } - - if (!Array.isArray(generatedSlides[generatorCount])) { - generatedSlides[generatorCount] = []; - } - - if (typeof childType !== 'function') { - // Add slide content to current generated slide - if (!Array.isArray(generatedSlides[generatorCount])) { - generatedSlides[generatorCount] = []; - } - generatedSlides[generatorCount].push(child); - // Check if it's a SpeakerNotes component - } else if (childType.name !== 'SpeakerNotes') { - // Add slide content to current generated slide - generatedSlides[generatorCount].push(child); - } - - }); - - const pageSize = { width: 1920, height: 1080 } - - return ( - <> - - {generatedSlides.map(d => ( - - {/* // */} - {/*
*/} - - {d} - - - - ))} - - ); -} diff --git a/components/display/SlidePage.jsx b/components/display/SlidePage.jsx deleted file mode 100644 index 96c5fe63..00000000 --- a/components/display/SlidePage.jsx +++ /dev/null @@ -1,281 +0,0 @@ -import React, { useEffect, useState } from "react"; -import { useRouter } from "next/router"; -import GlobalStyles from '@mui/material/GlobalStyles'; -// import Slide from "../components/Slide"; -import { Slide } from 'airview-mdx'; -import PresentationMode from "@/components/presentations/PresentationMode"; -import Swipeable from "@/components/presentations/Swipeable"; -import useEventListener from "../../hooks/useEventListener"; -import { useTotalPages } from "../../context/TotalPagesContext"; -import { useMode } from "../../context/ModeContext"; -import { useCurrentSlide } from "../../context/CurrentSlideContext"; -import { Storage } from "../../hooks/useStorage"; -import { MODES } from "../../constants/modes"; -import { Box } from '@mui/material'; - -import dynamic from 'next/dynamic' - -import Zoom from '@/components/presentations/Zoom'; - -const globalStyl2es = ` - - body, - html { - overflow: auto; - width: 100vw; - height: 100vw; - margin: 0; - padding: 0; - background-color: grey; - } - - @media print { - @page { - margin: 0mm 0mm 0mm 0mm; - size: 1280px 1080px; - } - } - - #slide { - display: flex; - overflow: hidden; - -webkit-overflow-scrolling: touch; - } - -`; - -const globalStyles = ` - - body - { - overflow: auto; - width: 100vw; - height: 100vh; - margin: 0; - padding: 0; - background-color: black; - } -`; - -export default dynamic(() => Promise.resolve(SlidePage), { - ssr: false, -}); - -function SlidePage({ children, next }) { - - // console.log('SlidePage:children: ', children) - - const { - // currentSlide, - setSlide, - steps, - currentStep, - setCurrentStep, - clearSteps, - } = useCurrentSlide(); - - const [pageCount, setPageCount] = useState(0) - const [currentSlide, setCurrentSlide] = useState(0) - const router = useRouter(); - const totalPages = useTotalPages(); - const { mode, setMode } = useMode(); - - const NEXT = [13, 32, 39]; - const PREV = 37; - const PRESENTER = 80; - let slideCount = 0; - - const navigate = ({ keyCode, altKey }) => { - // Handle Presentation Mode shortcut - if (altKey) { - if (keyCode === PRESENTER) { - if (mode === MODES.SPEAKER) { - setMode(MODES.SLIDESHOW); - // router.push( - // router.pathname, - // `${router.asPath.split("?")[0]}?format=ppt&mode=${MODES.SLIDESHOW}#${currentSlide}`, - // { shallow: true } - // ); - } else { - setMode(MODES.SPEAKER); - // router.push( - // router.pathname, - // `${router.asPath.split("?")[0]}?format=ppt&mode=${MODES.SPEAKER}#${currentSlide}`, - // { shallow: true } - // ); - } - return false; - } - } - - // Handle Previous page - if (keyCode === PREV) { - console.log('prev') - setCurrentSlide(currentSlide - 1) - - // if (router.query && router.pathname) { - // if (router.pathname > 1) { - // router.push(`${parseInt(router.pathname, 10) - 1}?mode=${mode}#999`); - // } - // } - return false; - } - - // Handle next page - if (NEXT.indexOf(keyCode) !== -1) { - setCurrentSlide(currentSlide + 1) - console.log('next', currentSlide) - - // if (router.query && router.pathname && next) { - // router.push(`${next}?format=ppt&mode=${mode}`); - // } - return false; - } - - // // Handle slide changes - // if (NEXT.indexOf(keyCode) !== -1) { - // // Do we have Steps inside the slide? Navigate those first - // if (steps.length > 0 && currentStep < steps.length - 1) - // return setCurrentStep((prevStep) => prevStep + 1); - - // // Otherwise go to next slide - - // setSlide((prevState) => { - // return prevState + 1; - // }); - // clearSteps(); - // } else if (keyCode === PREV) { - // // Do we have Steps inside the slide? Navigate those first - // if (steps.length > 0 && currentStep !== 0) - // return setCurrentStep((prevStep) => prevStep - 1); - - // // Otherwise go to prev slide - // setSlide((prevState) => { - // // router.push( - // // `${router.pathname}`, - // // `${router.pathname}?mode=${mode}#${prevState - 1}` - // // ); - // return prevState - 1; - // }); - // clearSteps(); - // } - }; - - // useEffect(() => { - // router.push( - // `${router.asPath}`, - // `${router.asPath.split("?")[0]}?format=ppt&mode=${mode}#${currentSlide}` - // ); - // }, [currentSlide, mode]); - - useEventListener("keydown", navigate); - - const swipeLeft = () => { - navigate({ keyCode: NEXT[0] }); - }; - - const swipeRight = () => { - navigate({ keyCode: PREV }); - }; - - const slideNotes = () => { - let generatorCount = 0; - let generatedNotes = []; - // Filter down children by only Slides - React.Children.map(children, (child) => { - // Check for
element to separate slides - const childType = child && child.props && (child.type || []); - if (childType && childType === "hr") { - generatorCount += 1; - return; - } - // Check if it's a SpeakerNotes component - if (typeof childType === 'function' && childType.name === 'SpeakerNotes') { - if (!Array.isArray(generatedNotes[generatorCount])) { - generatedNotes[generatorCount] = []; - } - generatedNotes[generatorCount].push(child); - } - }); - return generatedNotes; - }; - - function renderSlides() { - let generatedSlides = []; - let generatorCount = 0; - - // Filter down children by only Slides - React.Children.map(children, (child) => { - // Check for
element to separate slides - - console.log('Child: ', child) - - const childType = child && child.props && (child.type || []); - // console.log(childType) - if (childType && childType === "hr") { - generatorCount += 1; - return; - } - - if (!Array.isArray(generatedSlides[generatorCount])) { - generatedSlides[generatorCount] = []; - } - - if (typeof childType !== 'function') { - - // Add slide content to current generated slide - - if (!Array.isArray(generatedSlides[generatorCount])) { - generatedSlides[generatorCount] = []; - } - generatedSlides[generatorCount].push(child); - - // Check if it's a SpeakerNotes component - } else if (childType.name !== 'SpeakerNotes') { - // Add slide content to current generated slide - generatedSlides[generatorCount].push(child); - } - - }); - - // Get total slide count - slideCount = generatorCount; - - // setPageCount(generatorCount); - // // Return current slide - // if (currentSlide === 999) { - // router.push( - // router.pathname, - // `${router.pathname}?mode=${mode}#${slideCount}`, - // { shallow: true } - // ); - // setSlide(slideCount); - // } - // // // console.log('generatedSlides[currentSlide]: ', generatedSlides[currentSlide]) - return generatedSlides; - - // return {generatedSlides[currentSlide]}; - }; - - - const slides = renderSlides(); - const pageSize = { width: 1920, height: 1080 } - const ratio = pageSize.width / pageSize.height - return ( - - - - - - {/* */} - {/*
*/} - {/* */} - - {slides[currentSlide]} - - {/* */} - - - - ); -} diff --git a/components/display/TransitionPage.jsx b/components/display/TransitionPage.jsx deleted file mode 100644 index 63f03268..00000000 --- a/components/display/TransitionPage.jsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react' -import { motion } from 'framer-motion' - -export default function TransitionPage({ children }) { - return ( - - {children} - - ) -} diff --git a/components/editor/Editor.js b/components/editor/Editor.js deleted file mode 100644 index 78b94304..00000000 --- a/components/editor/Editor.js +++ /dev/null @@ -1,236 +0,0 @@ - -import '@mdxeditor/editor/style.css'; -import React from 'react'; -import { MDXEditor, system, realmPlugin, MdastImportVisitor, imagePlugin, codeBlockPlugin, diffSourcePlugin, headingsPlugin, frontmatterPlugin, listsPlugin, linkPlugin, linkDialogPlugin, quotePlugin, tablePlugin, thematicBreakPlugin, markdownShortcutPlugin, useCodeBlockEditorContext, toolbarPlugin, BlockTypeSelect, BoldItalicUnderlineToggles, UndoRedo, InsertTable, InsertCodeBlock, InsertFrontmatter, CreateLink, InsertThematicBreak, DiffSourceToggleWrapper } from '@mdxeditor/editor'; -import "@mdxeditor/editor/style.css"; -import { $createParagraphNode, $createTextNode, ElementNode } from "lexical"; - -// const { MDXEditor, codeBlockPlugin, diffSourcePlugin, headingsPlugin, frontmatterPlugin, listsPlugin, linkPlugin, linkDialogPlugin, quotePlugin, tablePlugin, thematicBreakPlugin, markdownShortcutPlugin, useCodeBlockEditorContext, toolbarPlugin, BlockTypeSelect, BoldItalicUnderlineToggles, UndoRedo, InsertTable, InsertCodeBlock, InsertFrontmatter, CreateLink, InsertThematicBreak, DiffSourceToggleWrapper } = await import('@mdxeditor/editor') -import { useState, useRef, createContext } from "react"; -import Button from '@mui/material/Button'; -const EditorStateContext = createContext(); -import store from '@/lib/redux/store' -import { AppBar, Toolbar, IconButton, Typography, MenuItem, Box, Alert, Grid, TextField } from '@mui/material'; -import SaveIcon from '@mui/icons-material/Save'; - - -const catchAllVisitor = { - testNode: () => true, - - visitNode: ({ mdastNode, actions, lexicalParent }) => { - const paragraph = $createParagraphNode(); - // This can be more sophisticated. For example, you can use the mdastNode.type - // to determine what kind of node you want to create. If you feel like it, - // you may even convert the mdastNode to html then to plain text. - // This will need additional dependencies, though. - try { - paragraph.append($createTextNode(mdastNode.children[0].value)); - } catch (err) { - console.debug('Editor:catchAllPlugin:mdastNode: ', mdastNode) - console.error('Editor:catchAllPlugin:error: ', err) - } - lexicalParent.append(paragraph); - } -}; - -const [catchAllPlugin] = realmPlugin({ - id: "catchAll", - systemSpec: system(() => ({})), - init: (realm) => { - realm.pubKey("addImportVisitor", catchAllVisitor); - } -}); - - -const PlainTextCodeEditorDescriptor = { - match: () => true, - priority: 0, - Editor: (props) => { - const cb = useCodeBlockEditorContext() - return ( -
e.nativeEvent.stopImmediatePropagation()}> -