From ddad7799dac3f8f8a07321cf6e0e3dbeb25579f6 Mon Sep 17 00:00:00 2001 From: aditya-tiwari-zs Date: Mon, 9 Dec 2024 12:57:04 +0530 Subject: [PATCH 1/4] added ui for cloud account feature --- .eslintrc.js | 53 + .eslintrc.json | 3 + .github/pull_request_template.md | 13 + .github/workflows/publish-image.yml | 69 + .gitignore | 40 + .prettierignore | 30 + .prettierrc | 10 + Dockerfile | 20 + Queries/CloudAccount/index.js | 22 + README.md | 37 +- components/Button/index.js | 25 + components/Cards/cloudAccountCard.js | 129 + components/EmptyPageComponent/index.js | 29 + components/ErrorCatcher/ErrorCatcher.js | 28 + components/ErrorComponent/index.js | 18 + components/HeaderComponents/index.js | 23 + components/Input/index.js | 63 + components/Label/index.js | 9 + .../Loaders/CompletePageLoader/index.js | 14 + components/Loaders/SimpleLoader/index.js | 39 + components/TextArea/index.js | 20 + constant.js | 10 + generateScript.js | 24 + hooks/cloudAccount/addCloudAccount.js | 89 + hooks/cloudAccount/getCloudAccountList.js | 27 + jsconfig.json | 7 + libs/configs.js | 1 + next.config.mjs | 4 + package-lock.json | 10705 ++++++++++++++++ package.json | 37 + partials/Header/index.js | 327 + partials/cloudAccount/createForm.js | 279 + postcss.config.mjs | 8 + public/applogoWithText.svg | 1 + src/app/cloud-accounts/create/page.js | 59 + src/app/cloud-accounts/page.js | 66 + src/app/favicon.ico | Bin 0 -> 64514 bytes src/app/fonts/GeistMonoVF.woff | Bin 0 -> 67864 bytes src/app/fonts/GeistVF.woff | Bin 0 -> 66268 bytes src/app/globals.css | 20 + src/app/layout.js | 39 + src/app/not-found.js | 27 + src/app/page.js | 3 + src/middleware.js | 18 + svg/appLogo.js | 97 + svg/aws.js | 43 + svg/azure.js | 71 + svg/cloudAccount.js | 51 + svg/emptyCloudAccount.js | 136 + svg/gcp.js | 45 + tailwind.config.js | 94 + utlis/apiClient.js | 51 + utlis/apiEndpoints.js | 1 + utlis/axiosInstance.js | 40 + utlis/helperFunc.js | 68 + 55 files changed, 13140 insertions(+), 2 deletions(-) create mode 100644 .eslintrc.js create mode 100644 .eslintrc.json create mode 100644 .github/pull_request_template.md create mode 100644 .github/workflows/publish-image.yml create mode 100644 .gitignore create mode 100644 .prettierignore create mode 100644 .prettierrc create mode 100644 Dockerfile create mode 100644 Queries/CloudAccount/index.js create mode 100644 components/Button/index.js create mode 100644 components/Cards/cloudAccountCard.js create mode 100644 components/EmptyPageComponent/index.js create mode 100644 components/ErrorCatcher/ErrorCatcher.js create mode 100644 components/ErrorComponent/index.js create mode 100644 components/HeaderComponents/index.js create mode 100644 components/Input/index.js create mode 100644 components/Label/index.js create mode 100644 components/Loaders/CompletePageLoader/index.js create mode 100644 components/Loaders/SimpleLoader/index.js create mode 100644 components/TextArea/index.js create mode 100644 constant.js create mode 100644 generateScript.js create mode 100644 hooks/cloudAccount/addCloudAccount.js create mode 100644 hooks/cloudAccount/getCloudAccountList.js create mode 100644 jsconfig.json create mode 100644 libs/configs.js create mode 100644 next.config.mjs create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 partials/Header/index.js create mode 100644 partials/cloudAccount/createForm.js create mode 100644 postcss.config.mjs create mode 100644 public/applogoWithText.svg create mode 100644 src/app/cloud-accounts/create/page.js create mode 100644 src/app/cloud-accounts/page.js create mode 100644 src/app/favicon.ico create mode 100644 src/app/fonts/GeistMonoVF.woff create mode 100644 src/app/fonts/GeistVF.woff create mode 100644 src/app/globals.css create mode 100644 src/app/layout.js create mode 100644 src/app/not-found.js create mode 100644 src/app/page.js create mode 100644 src/middleware.js create mode 100644 svg/appLogo.js create mode 100644 svg/aws.js create mode 100644 svg/azure.js create mode 100644 svg/cloudAccount.js create mode 100644 svg/emptyCloudAccount.js create mode 100644 svg/gcp.js create mode 100644 tailwind.config.js create mode 100644 utlis/apiClient.js create mode 100644 utlis/apiEndpoints.js create mode 100644 utlis/axiosInstance.js create mode 100644 utlis/helperFunc.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..07ab119 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,53 @@ +module.exports = { + env: { + browser: true, + es2021: true, + node: true, + es6: true, + jest: true, + }, + extends: ['standard', 'next/core-web-vitals', 'prettier', 'plugin:prettier/recommended'], + parser: '@babel/eslint-parser', + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + sourceType: 'module', + }, + settings: { + 'import/core-modules': ['dayjs', 'dayjs/plugin/timezone', 'dayjs/plugin/utc'], + }, + rules: { + 'prettier/prettier': 'error', + 'import/first': 'error', + 'import/newline-after-import': 'error', + 'import/no-extraneous-dependencies': ['error', { devDependencies: true }], + 'import/no-duplicates': 'error', + 'react/jsx-no-useless-fragment': 'off', + 'react-hooks/exhaustive-deps': 'off', + 'no-unused-vars': [ + 'error', + { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + ignoreRestSiblings: true, + }, + ], + 'no-restricted-syntax': [ + 'error', + { + selector: "CallExpression[callee.object.name='console'][callee.property.name!=/^(error)$/]", + message: 'Unexpected property on console object was called', + }, + ], + }, + plugins: ['prettier', 'react', 'promise'], + overrides: [ + { + files: ['**/__test__/**/*.js'], + rules: { + 'import/no-extraneous-dependencies': ['error', { devDependencies: true }], + }, + }, + ], +}; diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..bffb357 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..009cd6b --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,13 @@ +Closes Issue: # + +### Addition + +- put new additions here + +### Changes + +- put changes here + +### Fixes + +- put fixes here diff --git a/.github/workflows/publish-image.yml b/.github/workflows/publish-image.yml new file mode 100644 index 0000000..4cf23c9 --- /dev/null +++ b/.github/workflows/publish-image.yml @@ -0,0 +1,69 @@ +name: Publish zop-ui image + +on: + push: + tags: + - 'v*' + pull_request: + branches: + - main + +jobs: + prettier-and-lint: + name: 🧪 Prettier and Lint + runs-on: ubuntu-latest + + permissions: + contents: read + packages: write + + steps: + - name: Checkout Code + uses: actions/checkout@v3 + + - name: Set up Node + uses: actions/setup-node@v3 + with: + node-version: '18.x' + + - name: run linter + run: | + CI=false npm run lint + + - name: run prettier + run: | + CI=false npm run prettier:check + + - name: Build + run: npm run build + + publich-image: + if: ${{ startsWith(github.ref, 'refs/tags/v')}} + name: 🔨 Build and 🐳 Dockerize + needs: prettier-and-lint + runs-on: ubuntu-latest + + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Extract Release Tag + run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV + + - name: Log in to Docker Hub + uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a + with: + username: 'v4vikash' + password: ${{ secrets.DOCKER_HUB_PAT }} + + - name: Build and push Docker image + uses: docker/build-push-action@v4 + with: + push: true + context: . + file: Dockerfile + tags: zopdev/zop-api:${{ env.RELEASE_VERSION }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d32cc78 --- /dev/null +++ b/.gitignore @@ -0,0 +1,40 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..6e962f8 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,30 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build +.next + +# script +*.sh + +#vscode +.vscode + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..3f2956b --- /dev/null +++ b/.prettierrc @@ -0,0 +1,10 @@ +{ + "printWidth": 100, + "tabWidth": 2, + "useTabs": false, + "semi": true, + "singleQuote": true, + "bracketSpacing": true, + "bracketSameLine": false, + "proseWrap": "always" +} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c425dec --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +# Stage 1: Build the application +FROM node:18-alpine + +# Create and set the working directory for the app inside the container +WORKDIR /app + +# Copy the app's source code into the container +COPY . . + +# Install dependencies +RUN npm install + +# Build the Next.js application +RUN npm run build + +# Expose the port the app will run on +EXPOSE 3000 + +# Set the default command to run when the container starts (but don't run the app yet) +CMD ["npm", "run", "dev"] \ No newline at end of file diff --git a/Queries/CloudAccount/index.js b/Queries/CloudAccount/index.js new file mode 100644 index 0000000..3fa0740 --- /dev/null +++ b/Queries/CloudAccount/index.js @@ -0,0 +1,22 @@ +import apiClient from '../../utlis/apiClient'; +import { CLOUD_ACCOUNT_ENDPOINT } from '../../utlis/apiEndpoints'; + +const url = CLOUD_ACCOUNT_ENDPOINT; + +export const getCloudAccounts = async () => { + try { + const data = await apiClient.get(url); + return data; + } catch (error) { + throw new Error(error.response?.data?.error?.message || 'Failed to fetch cloud accounts'); + } +}; + +export const addCloudAccount = async (values) => { + try { + const response = await apiClient.post(url, values); + return response; + } catch (error) { + throw new Error(error.response?.data?.error?.message || 'Failed to add cloud account'); + } +}; diff --git a/README.md b/README.md index 247eb41..66bb426 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,36 @@ -# Zop CLI UI +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). -Welcome to the Zop CLI UI project! This project provides a user interface for the Zop command-line tool. +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/components/Button/index.js b/components/Button/index.js new file mode 100644 index 0000000..d1e2a1f --- /dev/null +++ b/components/Button/index.js @@ -0,0 +1,25 @@ +const Button = ({ variant, children, className: customClasses, ...props }) => { + const variantClasses = { + primary: + 'inline-flex items-center gap-x-1.5 rounded-md bg-primary-500 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-primary-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary-600 ', + secondary: + 'inline-flex items-center gap-x-1.5 rounded-md bg-black/5 px-3 py-2 text-sm font-semibold text-gray-500 shadow-sm hover:bg-black/10 ', + danger: + 'inline-flex items-center gap-x-1.5 rounded-md bg-red-500 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-400 ', + }; + + const className = `${variantClasses[variant] || variantClasses.primary} ${ + props?.disabled && 'opacity-40 hover:bg-primary-700 pointer-events-none' + }`; + return ( + + ); +}; + +export default Button; diff --git a/components/Cards/cloudAccountCard.js b/components/Cards/cloudAccountCard.js new file mode 100644 index 0000000..535fade --- /dev/null +++ b/components/Cards/cloudAccountCard.js @@ -0,0 +1,129 @@ +import React from 'react'; +import { formatTime } from '../../utlis/helperFunc'; +import { + PROVIDER_ICON_MAPPER, + // REFRESH_STATUS, + // colourCode, +} from '../../constant'; +// import DotWithProgress from "../Loader/DotWithProgression"; +// import { LogSvg } from "../../svg/sidebar/logs"; +// import IconButton from "../FormComponent/IconButton"; +// import RefreshIcon from "../../svg/refresh"; +// import Tooltip from "../FormComponent/toolTip"; +// import { useRouter } from "next/router"; + +const CloudAccountCard = ({ + item, + view, + // handleLogsOpen, + // handleRetryStatus, + // putToProvider, +}) => { + // const router = useRouter(); + + // useEffect(() => { + // if (item?.id) { + // router.prefetch(`/cloud-accounts/${item.id}/infrastructure`); + // } + // }, [item?.id, router]); + + // const handleRetry = (e, id) => { + // e.stopPropagation(); + // handleRetryStatus(id); + // }; + + // const latestLog = (e, data) => { + // e.stopPropagation(); + // handleLogsOpen(data); + // }; + + // const handleRedirect = () => { + // router.push(`/cloud-accounts/${item?.id}/infrastructure`); + // }; + + return ( +
+
+ {PROVIDER_ICON_MAPPER?.[item?.provider]} + {item?.name} + {/* {view === "cloudAccount" && ( + +
+ {REFRESH_STATUS?.includes(item?.status) ? ( + + ) : ( + <> + + + )} +
+
+ )} */} + {/* {item?.retry && ( + +
+ handleRetry(e, item?.id)} + > + + +
+
+ )} */} + {/* {view === "cloudAccount" && + REFRESH_STATUS?.includes(item?.status) && + item?.clusterId?.defaultId && ( +
+ + latestLog(e, item)}> + + + +
+ )} */} +
+ +
+ {item?.providerId} + {/* + + {item?.svcGroup === 1 + ? `1 App` + : item?.svcGroup > 1 + ? `${item?.svcGroup} Apps` + : ""} + + */} +
+ +
+ Updated At{' '} + {/* + {item?.updatedByIdentifier || item?.createdByIdentifier} + */} + {formatTime(item?.updatedAt)} +
+ + {/*
+ {formatTime(item?.updatedAt)} +
*/} +
+ ); +}; + +export default CloudAccountCard; diff --git a/components/EmptyPageComponent/index.js b/components/EmptyPageComponent/index.js new file mode 100644 index 0000000..ee8103a --- /dev/null +++ b/components/EmptyPageComponent/index.js @@ -0,0 +1,29 @@ +import React from 'react'; +import Link from 'next/link'; +import { PlusCircleIcon } from '@heroicons/react/20/solid'; + +const EmptyComponent = ({ imageComponent, redirectLink, buttonTitle, title, buttonIcon }) => { + return ( +
+
+
+ {imageComponent} +

{title}

+
+ {redirectLink && ( + + + + )} +
+
+ ); +}; + +export default EmptyComponent; diff --git a/components/ErrorCatcher/ErrorCatcher.js b/components/ErrorCatcher/ErrorCatcher.js new file mode 100644 index 0000000..648e173 --- /dev/null +++ b/components/ErrorCatcher/ErrorCatcher.js @@ -0,0 +1,28 @@ +'use client'; + +import React from 'react'; + +class ErrorBoundary extends React.Component { + constructor(props) { + super(props); + this.state = { hasError: false }; + } + + static getDerivedStateFromError() { + return { hasError: true }; + } + + componentDidCatch(error, info) { + console.error('Error caught in ErrorBoundary:', error, info); + } + + render() { + if (this.state.hasError) { + return this.props.fallback ||
Something went wrong.
; + } + + return this.props.children; + } +} + +export default ErrorBoundary; diff --git a/components/ErrorComponent/index.js b/components/ErrorComponent/index.js new file mode 100644 index 0000000..ce1fae0 --- /dev/null +++ b/components/ErrorComponent/index.js @@ -0,0 +1,18 @@ +import { ExclamationCircleIcon } from '@heroicons/react/20/solid'; + +export default function ErrorComponent({ errorText, className }) { + return ( +
+
+
+
+
+

+ {errorText} +

+
+
+
+ ); +} diff --git a/components/HeaderComponents/index.js b/components/HeaderComponents/index.js new file mode 100644 index 0000000..ced5356 --- /dev/null +++ b/components/HeaderComponents/index.js @@ -0,0 +1,23 @@ +import React from 'react'; + +const HeadingComponent = ({ title, subTitle, titleAction, actions, subTitleAction }) => { + return ( +
+
+
+

{title}

+ {titleAction && titleAction} +
+ {subTitle && ( +
+ Add User with Email and Specific Role +
+ )} + {subTitleAction && <>{subTitleAction}} +
+ {actions &&
{actions}
} +
+ ); +}; + +export default HeadingComponent; diff --git a/components/Input/index.js b/components/Input/index.js new file mode 100644 index 0000000..832f2f0 --- /dev/null +++ b/components/Input/index.js @@ -0,0 +1,63 @@ +import { useEffect, useState } from 'react'; + +const Input = ({ + error, + helperText, + helperTextClass, + errorTextClass, + className, + type, + value, + name, + required, + inputProps, + endAdornment, + testExp, + errorText, + ...props +}) => { + const [internalError, setInternalError] = useState(error); + useEffect(() => { + setInternalError(error); + }, [error]); + + return ( + <> +
+ { + if (testExp && !new RegExp(testExp).test(e.target.value)) { + setInternalError(true); + } else { + setInternalError(error); + } + if (props.onChange) props.onChange(e); + }} + /> + {endAdornment &&
{endAdornment}
} +
+ {helperText != null && ( +

{helperText}  

+ )} + {internalError && errorText && ( +

{errorText}  

+ )} + + ); +}; + +export default Input; diff --git a/components/Label/index.js b/components/Label/index.js new file mode 100644 index 0000000..c1481ee --- /dev/null +++ b/components/Label/index.js @@ -0,0 +1,9 @@ +function Label({ children, className, ...props }) { + return ( + + ); +} + +export default Label; diff --git a/components/Loaders/CompletePageLoader/index.js b/components/Loaders/CompletePageLoader/index.js new file mode 100644 index 0000000..b2ddb0e --- /dev/null +++ b/components/Loaders/CompletePageLoader/index.js @@ -0,0 +1,14 @@ +import React from 'react'; +import SimpleLoader from '../SimpleLoader'; + +/** + * this function is to display loader when query is fetching + * @returns loader + */ +export default function CompleteLoader() { + return ( +
+ +
+ ); +} diff --git a/components/Loaders/SimpleLoader/index.js b/components/Loaders/SimpleLoader/index.js new file mode 100644 index 0000000..e91a36a --- /dev/null +++ b/components/Loaders/SimpleLoader/index.js @@ -0,0 +1,39 @@ +import React from 'react'; + +export default function SimpleLoader({ + size = 34, + thickness = 2, + color, + secondaryCol, + secondaryColStr = 'text-gray-200', + colorStr = 'text-primary-500', +}) { + return ( +
+
+ +
+
+ ); +} diff --git a/components/TextArea/index.js b/components/TextArea/index.js new file mode 100644 index 0000000..5dd6851 --- /dev/null +++ b/components/TextArea/index.js @@ -0,0 +1,20 @@ +function Textarea({ error, helperText, className, ...props }) { + return ( + <> +