├── apps ├── .gitkeep ├── machines-validator │ ├── public │ │ ├── .gitkeep │ │ └── logo.png │ ├── index.d.ts │ ├── next-env.d.ts │ ├── specs │ │ └── index.spec.tsx │ ├── jest.config.ts │ ├── next.config.js │ ├── tsconfig.spec.json │ ├── tsconfig.json │ ├── .eslintrc.json │ ├── pages │ │ ├── _document.tsx │ │ ├── _app.tsx │ │ └── index.tsx │ ├── styles │ │ └── global.scss │ └── project.json └── machines-validator-e2e │ ├── src │ ├── support │ │ ├── app.po.ts │ │ ├── index.ts │ │ └── commands.ts │ ├── fixtures │ │ └── example.json │ └── integration │ │ └── app.spec.ts │ ├── .eslintrc.json │ ├── tsconfig.json │ ├── cypress.json │ └── project.json ├── libs ├── .gitkeep ├── shared-utilities │ ├── src │ │ ├── index.ts │ │ └── lib │ │ │ └── customers.ts │ ├── .babelrc │ ├── README.md │ ├── .eslintrc.json │ ├── jest.config.ts │ ├── tsconfig.spec.json │ ├── tsconfig.json │ ├── tsconfig.lib.json │ └── project.json ├── design-system │ ├── src │ │ ├── index.ts │ │ └── lib │ │ │ ├── variables.scss │ │ │ └── mixins.scss │ ├── .babelrc │ ├── README.md │ ├── .eslintrc.json │ ├── jest.config.ts │ ├── tsconfig.spec.json │ ├── tsconfig.json │ ├── tsconfig.lib.json │ └── project.json ├── common-layout │ ├── .babelrc │ ├── README.md │ ├── src │ │ ├── index.ts │ │ └── lib │ │ │ ├── form-container │ │ │ ├── form-container.module.scss │ │ │ ├── form-container.spec.tsx │ │ │ └── form-container.tsx │ │ │ ├── form-field │ │ │ ├── form-field.spec.tsx │ │ │ ├── form-field.module.scss │ │ │ └── form-field.tsx │ │ │ └── navbar │ │ │ ├── navbar.tsx │ │ │ ├── navbar.module.scss │ │ │ └── navbar.spec.tsx │ ├── .eslintrc.json │ ├── jest.config.ts │ ├── tsconfig.spec.json │ ├── tsconfig.json │ ├── tsconfig.lib.json │ └── project.json ├── shared-components │ ├── .babelrc │ ├── .storybook │ │ ├── preview.js │ │ ├── tsconfig.json │ │ └── main.js │ ├── README.md │ ├── src │ │ ├── index.ts │ │ └── lib │ │ │ ├── button │ │ │ ├── button.spec.tsx │ │ │ ├── button.stories.tsx │ │ │ ├── button.tsx │ │ │ └── button.module.scss │ │ │ ├── table │ │ │ ├── table.spec.tsx │ │ │ ├── table.module.scss │ │ │ └── table.tsx │ │ │ ├── checkbox │ │ │ ├── checkbox.spec.tsx │ │ │ ├── checkbox.tsx │ │ │ └── checkbox.module.scss │ │ │ └── dropdown │ │ │ ├── dropdown.stories.tsx │ │ │ ├── dropdown.module.scss │ │ │ ├── __snapshots__ │ │ │ └── dropdown.spec.tsx.snap │ │ │ ├── dropdown.spec.tsx │ │ │ └── dropdown.tsx │ ├── .eslintrc.json │ ├── jest.config.ts │ ├── tsconfig.spec.json │ ├── tsconfig.json │ ├── tsconfig.lib.json │ └── project.json └── shared-models │ ├── package.json │ ├── src │ ├── button.model.ts │ ├── index.ts │ ├── table.model.ts │ ├── dropdown.model.ts │ ├── navbar.model.ts │ └── customers.model.ts │ ├── .babelrc │ ├── tsconfig.lib.json │ ├── tsconfig.spec.json │ ├── README.md │ ├── .eslintrc.json │ ├── jest.config.ts │ ├── tsconfig.json │ └── project.json ├── tools ├── generators │ └── .gitkeep └── tsconfig.tools.json ├── .prettierrc ├── babel.config.json ├── .prettierignore ├── jest.preset.js ├── jest.config.ts ├── .vscode └── extensions.json ├── .editorconfig ├── .storybook ├── tsconfig.json └── main.js ├── README.md ├── zlab.code-workspace ├── workspace.json ├── .eslintrc.json ├── .gitignore ├── tsconfig.base.json ├── nx.json └── package.json /apps/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /libs/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tools/generators/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/machines-validator/public/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "babelrcRoots": ["*"] 3 | } 4 | -------------------------------------------------------------------------------- /libs/shared-utilities/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/customers' -------------------------------------------------------------------------------- /libs/design-system/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/design-system'; 2 | -------------------------------------------------------------------------------- /apps/machines-validator-e2e/src/support/app.po.ts: -------------------------------------------------------------------------------- 1 | export const getGreeting = () => cy.get('h1'); 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Add files here to ignore them from prettier formatting 2 | 3 | /dist 4 | /coverage 5 | -------------------------------------------------------------------------------- /jest.preset.js: -------------------------------------------------------------------------------- 1 | const nxPreset = require('@nrwl/jest/preset').default; 2 | 3 | module.exports = { ...nxPreset }; 4 | -------------------------------------------------------------------------------- /libs/common-layout/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@nrwl/next/babel" 4 | ], 5 | "plugins": [] 6 | } 7 | -------------------------------------------------------------------------------- /libs/design-system/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@nrwl/next/babel" 4 | ], 5 | "plugins": [] 6 | } 7 | -------------------------------------------------------------------------------- /libs/shared-components/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@nrwl/next/babel" 4 | ], 5 | "plugins": [] 6 | } 7 | -------------------------------------------------------------------------------- /libs/shared-utilities/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@nrwl/next/babel" 4 | ], 5 | "plugins": [] 6 | } 7 | -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | import { getJestProjects } from '@nrwl/jest'; 2 | 3 | export default { 4 | projects: getJestProjects(), 5 | }; 6 | -------------------------------------------------------------------------------- /libs/shared-models/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@zlab/shared-models", 3 | "version": "0.0.1", 4 | "type": "commonjs" 5 | } 6 | -------------------------------------------------------------------------------- /libs/shared-models/src/button.model.ts: -------------------------------------------------------------------------------- 1 | type ButtonType = 'primary' | 'secondary' | 'mute'; 2 | 3 | export type { ButtonType }; 4 | -------------------------------------------------------------------------------- /apps/machines-validator/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MRezaSafari/zlab-dashboard/HEAD/apps/machines-validator/public/logo.png -------------------------------------------------------------------------------- /apps/machines-validator-e2e/src/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io" 4 | } 5 | -------------------------------------------------------------------------------- /libs/shared-models/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@nrwl/web/babel", 5 | { 6 | "useBuiltIns": "usage" 7 | } 8 | ] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "nrwl.angular-console", 4 | "esbenp.prettier-vscode", 5 | "firsttris.vscode-jest-runner", 6 | "dbaeumer.vscode-eslint" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /libs/shared-models/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './navbar.model'; 2 | export * from './dropdown.model'; 3 | export * from './button.model'; 4 | export * from './table.model'; 5 | export * from './customers.model'; -------------------------------------------------------------------------------- /apps/machines-validator/index.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | declare module '*.svg' { 3 | const content: any; 4 | export const ReactComponent: any; 5 | export default content; 6 | } 7 | -------------------------------------------------------------------------------- /libs/common-layout/README.md: -------------------------------------------------------------------------------- 1 | # common-layout 2 | 3 | This library was generated with [Nx](https://nx.dev). 4 | 5 | ## Running unit tests 6 | 7 | Run `nx test common-layout` to execute the unit tests via [Jest](https://jestjs.io). 8 | -------------------------------------------------------------------------------- /libs/design-system/README.md: -------------------------------------------------------------------------------- 1 | # design-system 2 | 3 | This library was generated with [Nx](https://nx.dev). 4 | 5 | ## Running unit tests 6 | 7 | Run `nx test design-system` to execute the unit tests via [Jest](https://jestjs.io). 8 | -------------------------------------------------------------------------------- /libs/shared-components/.storybook/preview.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/no-webpack-loader-syntax 2 | // eslint-disable-next-line @nrwl/nx/enforce-module-boundaries 3 | import '../../../apps/machines-validator/styles/global.scss'; 4 | -------------------------------------------------------------------------------- /libs/common-layout/src/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Navbar } from './lib/navbar/navbar'; 2 | export { default as FormField } from './lib/form-field/form-field'; 3 | export { default as FormContainer } from './lib/form-container/form-container'; 4 | -------------------------------------------------------------------------------- /libs/shared-components/README.md: -------------------------------------------------------------------------------- 1 | # shared-components 2 | 3 | This library was generated with [Nx](https://nx.dev). 4 | 5 | ## Running unit tests 6 | 7 | Run `nx test shared-components` to execute the unit tests via [Jest](https://jestjs.io). 8 | -------------------------------------------------------------------------------- /libs/shared-utilities/README.md: -------------------------------------------------------------------------------- 1 | # shared-utilities 2 | 3 | This library was generated with [Nx](https://nx.dev). 4 | 5 | ## Running unit tests 6 | 7 | Run `nx test shared-utilities` to execute the unit tests via [Jest](https://jestjs.io). 8 | -------------------------------------------------------------------------------- /apps/machines-validator/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /libs/shared-components/src/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Button } from './lib/button/button'; 2 | export { default as Dropdown } from './lib/dropdown/dropdown'; 3 | export { default as Checkbox } from './lib/checkbox/checkbox'; 4 | export { default as Table } from './lib/table/table'; 5 | -------------------------------------------------------------------------------- /apps/machines-validator-e2e/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["plugin:cypress/recommended", "../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /libs/shared-models/src/table.model.ts: -------------------------------------------------------------------------------- 1 | type ColumnType = 'string' | 'boolean'; 2 | 3 | interface ColumnTemplate { 4 | title: string; 5 | width: string; 6 | type: ColumnType; 7 | valueKey: string; 8 | sortable: boolean; 9 | } 10 | 11 | export type { ColumnTemplate, ColumnType }; 12 | -------------------------------------------------------------------------------- /libs/common-layout/src/lib/form-container/form-container.module.scss: -------------------------------------------------------------------------------- 1 | @import '/libs/design-system/src/lib/mixins.scss'; 2 | 3 | 4 | .container { 5 | display: flex; 6 | gap: 2.5rem; 7 | flex-wrap: wrap; 8 | 9 | @include mobile(){ 10 | flex-direction: column; 11 | } 12 | } -------------------------------------------------------------------------------- /libs/shared-models/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "declaration": true, 6 | "types": [] 7 | }, 8 | "include": ["**/*.ts"], 9 | "exclude": ["jest.config.ts", "**/*.spec.ts", "**/*.test.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /libs/shared-models/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["jest.config.ts", "**/*.test.ts", "**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /apps/machines-validator-e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "sourceMap": false, 5 | "outDir": "../../dist/out-tsc", 6 | "allowJs": true, 7 | "types": ["cypress", "node"] 8 | }, 9 | "include": ["src/**/*.ts", "src/**/*.js"] 10 | } 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /libs/design-system/src/lib/variables.scss: -------------------------------------------------------------------------------- 1 | $font_primary: Roboto; 2 | $color_primary: #ffcf54; 3 | $color_secondary: #ff7433; 4 | $color_background: #1b1724; 5 | $color_title: #ecd8bd; 6 | $color_text: #fff; 7 | $color_danger: #ee5749; 8 | $color_warning: #fbc654; 9 | $color_info: #6abfed; 10 | $color_success: #03996e; 11 | -------------------------------------------------------------------------------- /libs/shared-models/README.md: -------------------------------------------------------------------------------- 1 | # shared-models 2 | 3 | This library was generated with [Nx](https://nx.dev). 4 | 5 | ## Building 6 | 7 | Run `nx build shared-models` to build the library. 8 | 9 | ## Running unit tests 10 | 11 | Run `nx test shared-models` to execute the unit tests via [Jest](https://jestjs.io). 12 | -------------------------------------------------------------------------------- /tools/tsconfig.tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "../dist/out-tsc/tools", 5 | "rootDir": ".", 6 | "module": "commonjs", 7 | "target": "es5", 8 | "types": ["node"], 9 | "importHelpers": false 10 | }, 11 | "include": ["**/*.ts"] 12 | } 13 | -------------------------------------------------------------------------------- /.storybook/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.base.json", 3 | "exclude": [ 4 | "../**/*.spec.js", 5 | "../**/*.test.js", 6 | "../**/*.spec.ts", 7 | "../**/*.test.ts", 8 | "../**/*.spec.tsx", 9 | "../**/*.test.tsx", 10 | "../**/*.spec.jsx", 11 | "../**/*.test.jsx" 12 | ], 13 | "include": ["../**/*"] 14 | } 15 | -------------------------------------------------------------------------------- /libs/shared-components/src/lib/button/button.spec.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@testing-library/react'; 2 | 3 | import Button from './button'; 4 | 5 | describe('Button', () => { 6 | it('should render successfully', () => { 7 | const { baseElement } = render(); 8 | expect(baseElement).toBeTruthy(); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /libs/shared-components/src/lib/table/table.spec.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@testing-library/react'; 2 | 3 | import Table from './table'; 4 | 5 | describe('Table', () => { 6 | it('should render successfully', () => { 7 | const { baseElement } = render(); 8 | expect(baseElement).toBeTruthy(); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /libs/design-system/src/lib/mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin mobile() { 2 | @media (max-width: 767px) { 3 | @content; 4 | } 5 | } 6 | 7 | @mixin tablet() { 8 | @media (min-width: 768px) and (max-width: 1319px) { 9 | @content; 10 | } 11 | } 12 | 13 | @mixin desktop() { 14 | @media (min-width: 1320px) { 15 | @content; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /libs/shared-models/src/dropdown.model.ts: -------------------------------------------------------------------------------- 1 | interface IDropdownProps { 2 | initialValue?: string; 3 | options: IDropdownOption[]; 4 | minimumSearchLength?: number; 5 | onChange: (value: string) => void; 6 | } 7 | 8 | interface IDropdownOption { 9 | id: number; 10 | label: string; 11 | value: string; 12 | } 13 | 14 | export type { IDropdownOption, IDropdownProps }; 15 | -------------------------------------------------------------------------------- /libs/common-layout/src/lib/form-field/form-field.spec.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@testing-library/react'; 2 | 3 | import FormField from './form-field'; 4 | 5 | describe('FormField', () => { 6 | it('should render successfully', () => { 7 | const { baseElement } = render(
); 8 | expect(baseElement).toBeTruthy(); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /libs/common-layout/src/lib/form-container/form-container.spec.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@testing-library/react'; 2 | 3 | import FormContainer from './form-container'; 4 | 5 | describe('FormContainer', () => { 6 | it('should render successfully', () => { 7 | const { baseElement } = render(
); 8 | expect(baseElement).toBeTruthy(); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /libs/shared-components/src/lib/checkbox/checkbox.spec.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@testing-library/react'; 2 | 3 | import Checkbox from './checkbox'; 4 | 5 | describe('Checkbox', () => { 6 | it('should render successfully', () => { 7 | const { baseElement } = render( {console.log(v)}} title="checkbox" />); 8 | expect(baseElement).toBeTruthy(); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /libs/shared-models/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /apps/machines-validator/specs/index.spec.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { render } from '@testing-library/react'; 4 | 5 | import Index from '../pages/index'; 6 | 7 | describe('Index', () => { 8 | it('should render successfully', () => { 9 | const { baseElement } = render(); 10 | expect(baseElement).toBeTruthy(); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /libs/common-layout/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /libs/design-system/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /libs/shared-utilities/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /.storybook/main.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | stories: [], 4 | addons: ['@storybook/addon-essentials'], 5 | // uncomment the property below if you want to apply some webpack config globally 6 | // webpackFinal: async (config, { configType }) => { 7 | // // Make whatever fine-grained changes you need that should apply to all storybook configs 8 | 9 | // // Return the altered config 10 | // return config; 11 | // }, 12 | }; 13 | -------------------------------------------------------------------------------- /libs/shared-components/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"], 3 | "ignorePatterns": ["!**/*"], 4 | "overrides": [ 5 | { 6 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 7 | "rules": {} 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "rules": {} 12 | }, 13 | { 14 | "files": ["*.js", "*.jsx"], 15 | "rules": {} 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /libs/shared-models/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'shared-models', 4 | preset: '../../jest.preset.js', 5 | globals: { 6 | 'ts-jest': { 7 | tsconfig: '/tsconfig.spec.json', 8 | }, 9 | }, 10 | transform: { 11 | '^.+\\.[tj]s$': 'ts-jest', 12 | }, 13 | moduleFileExtensions: ['ts', 'js', 'html'], 14 | coverageDirectory: '../../coverage/libs/shared-models', 15 | }; 16 | -------------------------------------------------------------------------------- /libs/shared-models/src/navbar.model.ts: -------------------------------------------------------------------------------- 1 | enum AnchorTagTarget { 2 | BLANK = '_blank', 3 | SELF = '_self', 4 | PARENT = '_parent', 5 | TOP = '_top', 6 | } 7 | 8 | interface INavbarProps { 9 | navbarItems: INavbar[]; 10 | } 11 | 12 | interface INavbar { 13 | id: number; 14 | title: string; 15 | href: string; 16 | target: AnchorTagTarget; 17 | } 18 | 19 | export { AnchorTagTarget }; 20 | 21 | export type { INavbar, INavbarProps }; 22 | -------------------------------------------------------------------------------- /libs/common-layout/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'common-layout', 4 | preset: '../../jest.preset.js', 5 | globals: { 6 | 'ts-jest': { 7 | tsconfig: '/tsconfig.spec.json', 8 | }, 9 | }, 10 | transform: { 11 | '^.+\\.[tj]sx?$': 'ts-jest', 12 | }, 13 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], 14 | coverageDirectory: '../../coverage/libs/common-layout', 15 | }; 16 | -------------------------------------------------------------------------------- /libs/design-system/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'design-system', 4 | preset: '../../jest.preset.js', 5 | globals: { 6 | 'ts-jest': { 7 | tsconfig: '/tsconfig.spec.json', 8 | }, 9 | }, 10 | transform: { 11 | '^.+\\.[tj]sx?$': 'ts-jest', 12 | }, 13 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], 14 | coverageDirectory: '../../coverage/libs/design-system', 15 | }; 16 | -------------------------------------------------------------------------------- /libs/shared-utilities/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'shared-utilities', 4 | preset: '../../jest.preset.js', 5 | globals: { 6 | 'ts-jest': { 7 | tsconfig: '/tsconfig.spec.json', 8 | }, 9 | }, 10 | transform: { 11 | '^.+\\.[tj]sx?$': 'ts-jest', 12 | }, 13 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], 14 | coverageDirectory: '../../coverage/libs/shared-utilities', 15 | }; 16 | -------------------------------------------------------------------------------- /libs/shared-components/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'shared-components', 4 | preset: '../../jest.preset.js', 5 | globals: { 6 | 'ts-jest': { 7 | tsconfig: '/tsconfig.spec.json', 8 | }, 9 | }, 10 | transform: { 11 | '^.+\\.[tj]sx?$': 'ts-jest', 12 | }, 13 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], 14 | coverageDirectory: '../../coverage/libs/shared-components', 15 | }; 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Z-Lab Machines Validator Dashboard 4 | dashboard for validating machines and serial numbers 5 | 6 | eveything in UI part made from scratch by myself. 7 | I created this as an interview assignment for zlab but they didn't even bother to check the code and started asking stupid questions. 8 | 9 | ## Techs: 10 | 11 | * NX Mono Repo 12 | * Nextjs 12 13 | * Jest 14 | * React-Testing-Library 15 | 16 | ## Live Demo 17 | https://zlab.vercel.app 18 | 19 | -------------------------------------------------------------------------------- /apps/machines-validator/jest.config.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | export default { 3 | displayName: 'machines-validator', 4 | preset: '../../jest.preset.js', 5 | transform: { 6 | '^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nrwl/react/plugins/jest', 7 | '^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nrwl/next/babel'] }], 8 | }, 9 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], 10 | coverageDirectory: '../../coverage/apps/machines-validator', 11 | }; 12 | -------------------------------------------------------------------------------- /apps/machines-validator/next.config.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/no-var-requires 2 | const withNx = require('@nrwl/next/plugins/with-nx'); 3 | 4 | /** 5 | * @type {import('@nrwl/next/plugins/with-nx').WithNxOptions} 6 | **/ 7 | const nextConfig = { 8 | nx: { 9 | // Set this to true if you would like to to use SVGR 10 | // See: https://github.com/gregberge/svgr 11 | svgr: false, 12 | }, 13 | }; 14 | 15 | module.exports = withNx(nextConfig); 16 | -------------------------------------------------------------------------------- /libs/common-layout/src/lib/form-field/form-field.module.scss: -------------------------------------------------------------------------------- 1 | @import '/libs/design-system/src/lib/variables.scss'; 2 | @import '/libs/design-system/src/lib/mixins.scss'; 3 | 4 | .container { 5 | display: flex; 6 | flex-direction: column; 7 | justify-content: space-between; 8 | gap: 1.5rem; 9 | > p { 10 | color: $color_title; 11 | font-size: 1.8rem; 12 | font-weight: 400; 13 | letter-spacing: .1rem; 14 | } 15 | } 16 | 17 | .field-container { 18 | } 19 | -------------------------------------------------------------------------------- /libs/shared-components/src/lib/button/button.stories.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Meta, 3 | Story, 4 | } from '@storybook/react/types-6-0'; 5 | 6 | import Button from './button'; 7 | 8 | export default { 9 | title: 'UI Components', 10 | component: Button, 11 | } as Meta; 12 | 13 | const Template: Story = ({ children }) => ; 14 | 15 | export const primary = Template.bind({}); 16 | primary.args = { children: 'Custom Button' }; 17 | primary.storyName = 'Button'; 18 | -------------------------------------------------------------------------------- /libs/common-layout/src/lib/form-container/form-container.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from 'react'; 2 | 3 | import styles from './form-container.module.scss'; 4 | 5 | /* eslint-disable-next-line */ 6 | export interface FormContainerProps { 7 | children: ReactNode; 8 | } 9 | 10 | export function FormContainer(props: FormContainerProps) { 11 | return ( 12 |
13 | {props.children} 14 |
15 | ); 16 | } 17 | 18 | export default FormContainer; 19 | -------------------------------------------------------------------------------- /libs/shared-models/src/customers.model.ts: -------------------------------------------------------------------------------- 1 | interface ICustomer { 2 | 3 | id: number; 4 | guid: string; 5 | customer: string; 6 | asset_type: string; 7 | serial_number: string; 8 | service_contract: boolean; 9 | warranty: boolean; 10 | 11 | } 12 | 13 | interface IFilters { 14 | customer: string; 15 | asset_type: string; 16 | service_contract: boolean; 17 | warranty: boolean; 18 | serial_number: string; 19 | } 20 | 21 | export type { ICustomer, IFilters }; 22 | -------------------------------------------------------------------------------- /libs/shared-utilities/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": [ 9 | "jest.config.ts", 10 | "**/*.test.ts", 11 | "**/*.spec.ts", 12 | "**/*.test.tsx", 13 | "**/*.spec.tsx", 14 | "**/*.test.js", 15 | "**/*.spec.js", 16 | "**/*.test.jsx", 17 | "**/*.spec.jsx", 18 | "**/*.d.ts" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /apps/machines-validator-e2e/cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "fileServerFolder": ".", 3 | "fixturesFolder": "./src/fixtures", 4 | "integrationFolder": "./src/integration", 5 | "modifyObstructiveCode": false, 6 | "supportFile": "./src/support/index.ts", 7 | "pluginsFile": false, 8 | "video": true, 9 | "videosFolder": "../../dist/cypress/apps/machines-validator-e2e/videos", 10 | "screenshotsFolder": "../../dist/cypress/apps/machines-validator-e2e/screenshots", 11 | "chromeWebSecurity": false 12 | } 13 | -------------------------------------------------------------------------------- /zlab.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": { 8 | "files.exclude": { 9 | "**/.git": true, 10 | "**/.svn": true, 11 | "**/.hg": true, 12 | "**/CVS": true, 13 | "**/.DS_Store": true, 14 | "**/Thumbs.db": true, 15 | "**/node_modules": true, 16 | "node_modules": true, 17 | "**/dist": true, 18 | "dist": true 19 | }, 20 | "explorerExclude.backup": null, 21 | "typescript.tsdk": "node_modules/typescript/lib" 22 | } 23 | } -------------------------------------------------------------------------------- /workspace.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/nx/schemas/workspace-schema.json", 3 | "version": 2, 4 | "projects": { 5 | "common-layout": "libs/common-layout", 6 | "design-system": "libs/design-system", 7 | "machines-validator": "apps/machines-validator", 8 | "machines-validator-e2e": "apps/machines-validator-e2e", 9 | "shared-components": "libs/shared-components", 10 | "shared-models": "libs/shared-models", 11 | "shared-utilities": "libs/shared-utilities" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/machines-validator-e2e/src/integration/app.spec.ts: -------------------------------------------------------------------------------- 1 | import { getGreeting } from '../support/app.po'; 2 | 3 | describe('machines-validator', () => { 4 | beforeEach(() => cy.visit('/')); 5 | 6 | it('should display welcome message', () => { 7 | // Custom command example, see `../support/commands.ts` file 8 | cy.login('my-email@something.com', 'myPassword'); 9 | 10 | // Function helper example, see `../support/app.po.ts` file 11 | getGreeting().contains('Welcome machines-validator'); 12 | }); 13 | }); 14 | -------------------------------------------------------------------------------- /apps/machines-validator/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"], 7 | "jsx": "react" 8 | }, 9 | "include": [ 10 | "jest.config.ts", 11 | "**/*.test.ts", 12 | "**/*.spec.ts", 13 | "**/*.test.tsx", 14 | "**/*.spec.tsx", 15 | "**/*.test.js", 16 | "**/*.spec.js", 17 | "**/*.test.jsx", 18 | "**/*.spec.jsx", 19 | "**/*.d.ts" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "ignorePatterns": ["**/*"], 4 | "plugins": ["@nrwl/nx"], 5 | "overrides": [ 6 | { 7 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"] 8 | }, 9 | { 10 | "files": ["*.ts", "*.tsx"], 11 | "extends": ["plugin:@nrwl/nx/typescript"], 12 | "rules": { 13 | "@typescript-eslint/ban-ts-comment": "off" 14 | } 15 | }, 16 | { 17 | "files": ["*.js", "*.jsx"], 18 | "extends": ["plugin:@nrwl/nx/javascript"], 19 | "rules": {} 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /libs/common-layout/src/lib/form-field/form-field.tsx: -------------------------------------------------------------------------------- 1 | import { ReactNode } from 'react'; 2 | 3 | import styles from './form-field.module.scss'; 4 | 5 | /* eslint-disable-next-line */ 6 | export interface FormFieldProps { 7 | children: ReactNode; 8 | title: string; 9 | } 10 | 11 | export function FormField(props: FormFieldProps) { 12 | return ( 13 |
14 |

{props.title}

15 |
{props.children}
16 |
17 | ); 18 | } 19 | 20 | export default FormField; 21 | -------------------------------------------------------------------------------- /libs/shared-models/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "commonjs", 5 | "forceConsistentCasingInFileNames": true, 6 | "strict": true, 7 | "noImplicitOverride": true, 8 | "noPropertyAccessFromIndexSignature": true, 9 | "noImplicitReturns": true, 10 | "noFallthroughCasesInSwitch": true 11 | }, 12 | "files": [], 13 | "include": [], 14 | "references": [ 15 | { 16 | "path": "./tsconfig.lib.json" 17 | }, 18 | { 19 | "path": "./tsconfig.spec.json" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /libs/shared-components/.storybook/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "emitDecoratorMetadata": true, 5 | "outDir": "" 6 | }, 7 | "files": [ 8 | "../../../node_modules/@nrwl/react/typings/styled-jsx.d.ts", 9 | "../../../node_modules/@nrwl/react/typings/cssmodule.d.ts", 10 | "../../../node_modules/@nrwl/react/typings/image.d.ts" 11 | ], 12 | "exclude": [ 13 | "../**/*.spec.ts", 14 | "../**/*.spec.js", 15 | "../**/*.spec.tsx", 16 | "../**/*.spec.jsx" 17 | ], 18 | "include": ["../src/**/*", "*.js"] 19 | } 20 | -------------------------------------------------------------------------------- /apps/machines-validator/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "jsx": "preserve", 5 | "allowJs": true, 6 | "esModuleInterop": true, 7 | "allowSyntheticDefaultImports": true, 8 | "strict": false, 9 | "forceConsistentCasingInFileNames": true, 10 | "noEmit": true, 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "incremental": true, 14 | "types": ["jest", "node"] 15 | }, 16 | "include": ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "next-env.d.ts"], 17 | "exclude": ["node_modules", "jest.config.ts"] 18 | } 19 | -------------------------------------------------------------------------------- /libs/common-layout/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "files": [ 9 | "../../node_modules/@nrwl/react/typings/cssmodule.d.ts", 10 | "../../node_modules/@nrwl/next/typings/image.d.ts" 11 | ], 12 | "include": [ 13 | "jest.config.ts", 14 | "**/*.test.ts", 15 | "**/*.spec.ts", 16 | "**/*.test.tsx", 17 | "**/*.spec.tsx", 18 | "**/*.test.js", 19 | "**/*.spec.js", 20 | "**/*.test.jsx", 21 | "**/*.spec.jsx", 22 | "**/*.d.ts" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /libs/design-system/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "files": [ 9 | "../../node_modules/@nrwl/react/typings/cssmodule.d.ts", 10 | "../../node_modules/@nrwl/next/typings/image.d.ts" 11 | ], 12 | "include": [ 13 | "jest.config.ts", 14 | "**/*.test.ts", 15 | "**/*.spec.ts", 16 | "**/*.test.tsx", 17 | "**/*.spec.tsx", 18 | "**/*.test.js", 19 | "**/*.spec.js", 20 | "**/*.test.jsx", 21 | "**/*.spec.jsx", 22 | "**/*.d.ts" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /libs/shared-components/src/lib/button/button.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { ButtonType } from '@zlab/shared-models'; 4 | 5 | import styles from './button.module.scss'; 6 | 7 | export interface ButtonProps { 8 | children: React.ReactNode; 9 | onClick?: () => void; 10 | type?: ButtonType; 11 | } 12 | 13 | export function Button({ children, onClick, type = 'primary' }: ButtonProps) { 14 | return ( 15 | 21 | ); 22 | } 23 | 24 | export default Button; 25 | -------------------------------------------------------------------------------- /libs/shared-components/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "module": "commonjs", 6 | "types": ["jest", "node", "testing-library__jest-dom"] 7 | }, 8 | "files": [ 9 | "../../node_modules/@nrwl/react/typings/cssmodule.d.ts", 10 | "../../node_modules/@nrwl/next/typings/image.d.ts" 11 | ], 12 | "include": [ 13 | "jest.config.ts", 14 | "**/*.test.ts", 15 | "**/*.spec.ts", 16 | "**/*.test.tsx", 17 | "**/*.spec.tsx", 18 | "**/*.test.js", 19 | "**/*.spec.js", 20 | "**/*.test.jsx", 21 | "**/*.spec.jsx", 22 | "**/*.d.ts" 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | 8 | # dependencies 9 | node_modules 10 | 11 | # IDEs and editors 12 | /.idea 13 | .project 14 | .classpath 15 | .c9/ 16 | *.launch 17 | .settings/ 18 | *.sublime-workspace 19 | 20 | # IDE - VSCode 21 | .vscode/* 22 | !.vscode/settings.json 23 | !.vscode/tasks.json 24 | !.vscode/launch.json 25 | !.vscode/extensions.json 26 | 27 | # misc 28 | /.sass-cache 29 | /connect.lock 30 | /coverage 31 | /libpeerconnection.log 32 | npm-debug.log 33 | yarn-error.log 34 | testem.log 35 | /typings 36 | 37 | # System Files 38 | .DS_Store 39 | Thumbs.db 40 | -------------------------------------------------------------------------------- /libs/common-layout/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "jsx": "react-jsx", 5 | "allowJs": true, 6 | "esModuleInterop": true, 7 | "allowSyntheticDefaultImports": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "strict": true, 10 | "noImplicitOverride": true, 11 | "noPropertyAccessFromIndexSignature": true, 12 | "noImplicitReturns": true, 13 | "noFallthroughCasesInSwitch": true 14 | }, 15 | "files": [], 16 | "include": [], 17 | "references": [ 18 | { 19 | "path": "./tsconfig.lib.json" 20 | }, 21 | { 22 | "path": "./tsconfig.spec.json" 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /libs/design-system/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "jsx": "react-jsx", 5 | "allowJs": true, 6 | "esModuleInterop": true, 7 | "allowSyntheticDefaultImports": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "strict": true, 10 | "noImplicitOverride": true, 11 | "noPropertyAccessFromIndexSignature": true, 12 | "noImplicitReturns": true, 13 | "noFallthroughCasesInSwitch": true 14 | }, 15 | "files": [], 16 | "include": [], 17 | "references": [ 18 | { 19 | "path": "./tsconfig.lib.json" 20 | }, 21 | { 22 | "path": "./tsconfig.spec.json" 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /libs/shared-utilities/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "jsx": "react-jsx", 5 | "allowJs": true, 6 | "esModuleInterop": true, 7 | "allowSyntheticDefaultImports": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "strict": true, 10 | "noImplicitOverride": true, 11 | "noPropertyAccessFromIndexSignature": true, 12 | "noImplicitReturns": true, 13 | "noFallthroughCasesInSwitch": true 14 | }, 15 | "files": [], 16 | "include": [], 17 | "references": [ 18 | { 19 | "path": "./tsconfig.lib.json" 20 | }, 21 | { 22 | "path": "./tsconfig.spec.json" 23 | } 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /apps/machines-validator-e2e/src/support/index.ts: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands'; 18 | -------------------------------------------------------------------------------- /libs/common-layout/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "types": [ 6 | "node" 7 | ] 8 | }, 9 | "files": [ 10 | "../../node_modules/@nrwl/react/typings/cssmodule.d.ts", 11 | "../../node_modules/@nrwl/next/typings/image.d.ts", 12 | ], 13 | "exclude": [ 14 | "jest.config.ts", 15 | "**/*.spec.ts", 16 | "**/*.test.ts", 17 | "**/*.spec.tsx", 18 | "**/*.test.tsx", 19 | "**/*.spec.js", 20 | "**/*.test.js", 21 | "**/*.spec.jsx", 22 | "**/*.test.jsx" 23 | ], 24 | "include": [ 25 | "**/*.js", 26 | "**/*.jsx", 27 | "**/*.ts", 28 | "**/*.tsx" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /libs/design-system/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "types": [ 6 | "node" 7 | ] 8 | }, 9 | "files": [ 10 | "../../node_modules/@nrwl/react/typings/cssmodule.d.ts", 11 | "../../node_modules/@nrwl/next/typings/image.d.ts" 12 | ], 13 | "exclude": [ 14 | "jest.config.ts", 15 | "**/*.spec.ts", 16 | "**/*.test.ts", 17 | "**/*.spec.tsx", 18 | "**/*.test.tsx", 19 | "**/*.spec.js", 20 | "**/*.test.js", 21 | "**/*.spec.jsx", 22 | "**/*.test.jsx" 23 | ], 24 | "include": [ 25 | "**/*.js", 26 | "**/*.jsx", 27 | "**/*.ts", 28 | "**/*.tsx" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /libs/shared-utilities/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "types": [ 6 | "node" 7 | ] 8 | }, 9 | "files": [ 10 | "../../node_modules/@nrwl/react/typings/cssmodule.d.ts", 11 | "../../node_modules/@nrwl/next/typings/image.d.ts" 12 | ], 13 | "exclude": [ 14 | "jest.config.ts", 15 | "**/*.spec.ts", 16 | "**/*.test.ts", 17 | "**/*.spec.tsx", 18 | "**/*.test.tsx", 19 | "**/*.spec.js", 20 | "**/*.test.js", 21 | "**/*.spec.jsx", 22 | "**/*.test.jsx" 23 | ], 24 | "include": [ 25 | "**/*.js", 26 | "**/*.jsx", 27 | "**/*.ts", 28 | "**/*.tsx" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /apps/machines-validator/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "plugin:@nrwl/nx/react-typescript", 4 | "../../.eslintrc.json", 5 | "next", 6 | "next/core-web-vitals" 7 | ], 8 | "ignorePatterns": ["!**/*"], 9 | "overrides": [ 10 | { 11 | "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], 12 | "rules": { 13 | "@next/next/no-html-link-for-pages": [ 14 | "error", 15 | "apps/machines-validator/pages" 16 | ] 17 | } 18 | }, 19 | { 20 | "files": ["*.ts", "*.tsx"], 21 | "rules": {} 22 | }, 23 | { 24 | "files": ["*.js", "*.jsx"], 25 | "rules": {} 26 | } 27 | ], 28 | "env": { 29 | "jest": true 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /libs/common-layout/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 3 | "sourceRoot": "libs/common-layout/src", 4 | "projectType": "library", 5 | "tags": [], 6 | "targets": { 7 | "lint": { 8 | "executor": "@nrwl/linter:eslint", 9 | "outputs": ["{options.outputFile}"], 10 | "options": { 11 | "lintFilePatterns": ["libs/common-layout/**/*.{ts,tsx,js,jsx}"] 12 | } 13 | }, 14 | "test": { 15 | "executor": "@nrwl/jest:jest", 16 | "outputs": ["coverage/libs/common-layout"], 17 | "options": { 18 | "jestConfig": "libs/common-layout/jest.config.ts", 19 | "passWithNoTests": true 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /libs/design-system/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 3 | "sourceRoot": "libs/design-system/src", 4 | "projectType": "library", 5 | "tags": [], 6 | "targets": { 7 | "lint": { 8 | "executor": "@nrwl/linter:eslint", 9 | "outputs": ["{options.outputFile}"], 10 | "options": { 11 | "lintFilePatterns": ["libs/design-system/**/*.{ts,tsx,js,jsx}"] 12 | } 13 | }, 14 | "test": { 15 | "executor": "@nrwl/jest:jest", 16 | "outputs": ["coverage/libs/design-system"], 17 | "options": { 18 | "jestConfig": "libs/design-system/jest.config.ts", 19 | "passWithNoTests": true 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /libs/shared-utilities/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 3 | "sourceRoot": "libs/shared-utilities/src", 4 | "projectType": "library", 5 | "tags": [], 6 | "targets": { 7 | "lint": { 8 | "executor": "@nrwl/linter:eslint", 9 | "outputs": ["{options.outputFile}"], 10 | "options": { 11 | "lintFilePatterns": ["libs/shared-utilities/**/*.{ts,tsx,js,jsx}"] 12 | } 13 | }, 14 | "test": { 15 | "executor": "@nrwl/jest:jest", 16 | "outputs": ["coverage/libs/shared-utilities"], 17 | "options": { 18 | "jestConfig": "libs/shared-utilities/jest.config.ts", 19 | "passWithNoTests": true 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /libs/shared-components/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base.json", 3 | "compilerOptions": { 4 | "jsx": "react-jsx", 5 | "allowJs": true, 6 | "esModuleInterop": true, 7 | "allowSyntheticDefaultImports": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "strict": true, 10 | "noImplicitOverride": true, 11 | "noPropertyAccessFromIndexSignature": true, 12 | "noImplicitReturns": true, 13 | "noFallthroughCasesInSwitch": true 14 | }, 15 | "files": [], 16 | "include": [], 17 | "references": [ 18 | { 19 | "path": "./tsconfig.lib.json" 20 | }, 21 | { 22 | "path": "./tsconfig.spec.json" 23 | }, 24 | { 25 | "path": "./.storybook/tsconfig.json" 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /libs/shared-components/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc", 5 | "types": ["node"] 6 | }, 7 | "files": [ 8 | "../../node_modules/@nrwl/react/typings/cssmodule.d.ts", 9 | "../../node_modules/@nrwl/next/typings/image.d.ts" 10 | ], 11 | "exclude": [ 12 | "jest.config.ts", 13 | "**/*.spec.ts", 14 | "**/*.test.ts", 15 | "**/*.spec.tsx", 16 | "**/*.test.tsx", 17 | "**/*.spec.js", 18 | "**/*.test.js", 19 | "**/*.spec.jsx", 20 | "**/*.test.jsx", 21 | "**/*.stories.ts", 22 | "**/*.stories.js", 23 | "**/*.stories.jsx", 24 | "**/*.stories.tsx" 25 | ], 26 | "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"] 27 | } 28 | -------------------------------------------------------------------------------- /libs/shared-components/.storybook/main.js: -------------------------------------------------------------------------------- 1 | const rootMain = require('../../../.storybook/main'); 2 | 3 | module.exports = { 4 | ...rootMain, 5 | 6 | core: { ...rootMain.core, builder: 'webpack5' }, 7 | 8 | stories: [ 9 | ...rootMain.stories, 10 | '../src/lib/**/*.stories.mdx', 11 | '../src/lib/**/*.stories.@(js|jsx|ts|tsx)', 12 | ], 13 | addons: [...rootMain.addons, '@nrwl/react/plugins/storybook'], 14 | webpackFinal: async (config, { configType }) => { 15 | // apply any global webpack configs that might have been specified in .storybook/main.js 16 | if (rootMain.webpackFinal) { 17 | config = await rootMain.webpackFinal(config, { configType }); 18 | } 19 | 20 | // add your own webpack tweaks if needed 21 | 22 | return config; 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /libs/shared-components/src/lib/checkbox/checkbox.tsx: -------------------------------------------------------------------------------- 1 | import styles from './checkbox.module.scss'; 2 | 3 | /* eslint-disable-next-line */ 4 | export interface CheckboxProps { 5 | initialValue?: boolean; 6 | title: string; 7 | id: string; 8 | onChange: (checked: boolean) => void; 9 | } 10 | 11 | export function Checkbox({ id, initialValue, onChange, title }: CheckboxProps) { 12 | return ( 13 |
14 | onChange(e.target.checked)} 19 | className={styles['switch']} 20 | /> 21 | 22 |
23 | ); 24 | } 25 | 26 | export default Checkbox; 27 | -------------------------------------------------------------------------------- /apps/machines-validator/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import Document, { 2 | Head, 3 | Html, 4 | Main, 5 | NextScript, 6 | } from 'next/document'; 7 | 8 | export default class CustomDocument extends Document { 9 | render() { 10 | return ( 11 | 12 | 13 | 14 | 19 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /apps/machines-validator-e2e/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 3 | "sourceRoot": "apps/machines-validator-e2e/src", 4 | "projectType": "application", 5 | "targets": { 6 | "e2e": { 7 | "executor": "@nrwl/cypress:cypress", 8 | "options": { 9 | "cypressConfig": "apps/machines-validator-e2e/cypress.json", 10 | "devServerTarget": "machines-validator:serve:development" 11 | }, 12 | "configurations": { 13 | "production": { 14 | "devServerTarget": "machines-validator:serve:production" 15 | } 16 | } 17 | }, 18 | "lint": { 19 | "executor": "@nrwl/linter:eslint", 20 | "outputs": ["{options.outputFile}"], 21 | "options": { 22 | "lintFilePatterns": ["apps/machines-validator-e2e/**/*.{js,ts}"] 23 | } 24 | } 25 | }, 26 | "tags": [], 27 | "implicitDependencies": ["machines-validator"] 28 | } 29 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "rootDir": ".", 5 | "sourceMap": true, 6 | "declaration": false, 7 | "moduleResolution": "node", 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "importHelpers": true, 11 | "target": "es2015", 12 | "module": "esnext", 13 | "lib": ["es2017", "dom"], 14 | "skipLibCheck": true, 15 | "skipDefaultLibCheck": true, 16 | "baseUrl": ".", 17 | "paths": { 18 | "@zlab/common-layout": ["libs/common-layout/src/index.ts"], 19 | "@zlab/design-system": ["libs/design-system/src/index.ts"], 20 | "@zlab/shared-components": ["libs/shared-components/src/index.ts"], 21 | "@zlab/shared-models": ["libs/shared-models/src/index.ts"], 22 | "@zlab/shared-utilities": ["libs/shared-utilities/src/index.ts"], 23 | "@zlab/ui": ["libs/ui/src/index.ts"] 24 | } 25 | }, 26 | "exclude": ["node_modules", "tmp"] 27 | } 28 | -------------------------------------------------------------------------------- /libs/shared-components/src/lib/dropdown/dropdown.stories.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Meta, 3 | Story, 4 | } from '@storybook/react/types-6-0'; 5 | import { IDropdownProps } from '@zlab/shared-models'; 6 | 7 | import Dropdown from './dropdown'; 8 | 9 | export default { 10 | title: 'UI Components', 11 | component: Dropdown, 12 | } as Meta; 13 | 14 | const Template: Story = ({ options, onChange, minimumSearchLength }: IDropdownProps) => ( 15 | 20 | ); 21 | 22 | export const primary = Template.bind({}); 23 | primary.args = { 24 | options: [ 25 | { id: 1, label: 'Kassulke & Sohn', value: 'Kassulke & Sohn' }, 26 | { id: 2, label: 'Bayer-Bergnaum', value: 'Bayer-Bergnaum' }, 27 | { id: 3, label: 'Schinner Group', value: 'Schinner Group' }, 28 | ], 29 | onChange: (value: string) => { 30 | console.log(value); 31 | }, 32 | minimumSearchLength: 0, 33 | }; 34 | primary.storyName = 'Dropdown'; 35 | -------------------------------------------------------------------------------- /libs/shared-utilities/src/lib/customers.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ICustomer, 3 | IDropdownOption, 4 | IFilters, 5 | } from '@zlab/shared-models'; 6 | 7 | const extractUniqueValues = ( 8 | customers: ICustomer[], 9 | key: keyof ICustomer 10 | ): string[] => { 11 | return [...new Set(customers.map((c) => c[key]))] as string[]; 12 | }; 13 | 14 | const generateOptionsByKey = ( 15 | customers: ICustomer[], 16 | key: keyof ICustomer 17 | ): IDropdownOption[] => { 18 | const uniqueValues = extractUniqueValues(customers, key); 19 | 20 | return uniqueValues.map((u, i) => { 21 | return { 22 | id: i, 23 | label: u, 24 | value: u, 25 | }; 26 | }); 27 | }; 28 | 29 | const filterCustomers = (customers: ICustomer[], filters: IFilters) => { 30 | 31 | 32 | return customers.filter(customer => { 33 | return Object.keys(filters).every((filter: string) => { 34 | // @ts-ignore 35 | return filters[filter] === customer[filter] 36 | }) 37 | }) 38 | }; 39 | 40 | export { filterCustomers, generateOptionsByKey }; 41 | -------------------------------------------------------------------------------- /libs/shared-models/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 3 | "sourceRoot": "libs/shared-models/src", 4 | "projectType": "library", 5 | "targets": { 6 | "build": { 7 | "executor": "@nrwl/js:tsc", 8 | "outputs": ["{options.outputPath}"], 9 | "options": { 10 | "outputPath": "dist/libs/shared-models", 11 | "main": "libs/shared-models/src/index.ts", 12 | "tsConfig": "libs/shared-models/tsconfig.lib.json", 13 | "assets": ["libs/shared-models/*.md"] 14 | } 15 | }, 16 | "lint": { 17 | "executor": "@nrwl/linter:eslint", 18 | "outputs": ["{options.outputFile}"], 19 | "options": { 20 | "lintFilePatterns": ["libs/shared-models/**/*.ts"] 21 | } 22 | }, 23 | "test": { 24 | "executor": "@nrwl/jest:jest", 25 | "outputs": ["coverage/libs/shared-models"], 26 | "options": { 27 | "jestConfig": "libs/shared-models/jest.config.ts", 28 | "passWithNoTests": true 29 | } 30 | } 31 | }, 32 | "tags": [] 33 | } 34 | -------------------------------------------------------------------------------- /nx.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/nx/schemas/nx-schema.json", 3 | "npmScope": "zlab", 4 | "affected": { 5 | "defaultBase": "main" 6 | }, 7 | "implicitDependencies": { 8 | "package.json": { 9 | "dependencies": "*", 10 | "devDependencies": "*" 11 | }, 12 | ".eslintrc.json": "*" 13 | }, 14 | "tasksRunnerOptions": { 15 | "default": { 16 | "runner": "nx/tasks-runners/default", 17 | "options": { 18 | "cacheableOperations": [ 19 | "build", 20 | "lint", 21 | "test", 22 | "e2e", 23 | "build-storybook" 24 | ] 25 | } 26 | } 27 | }, 28 | "targetDefaults": { 29 | "build": { 30 | "dependsOn": ["^build"] 31 | } 32 | }, 33 | "generators": { 34 | "@nrwl/react": { 35 | "application": { 36 | "babel": true 37 | } 38 | }, 39 | "@nrwl/next": { 40 | "application": { 41 | "style": "scss", 42 | "linter": "eslint" 43 | } 44 | } 45 | }, 46 | "defaultProject": "machines-validator" 47 | } 48 | -------------------------------------------------------------------------------- /libs/shared-components/src/lib/button/button.module.scss: -------------------------------------------------------------------------------- 1 | @import '/libs/design-system/src/lib/variables.scss'; 2 | @import '/libs/design-system/src/lib/mixins.scss'; 3 | 4 | .button-container { 5 | background: #e0fbed; 6 | border: 0.1rem solid #bcf3d4; 7 | border-radius: 0.8rem; 8 | padding: 0.6rem 0.8rem; 9 | display: flex; 10 | align-items: center; 11 | justify-content: center; 12 | gap: .4rem; 13 | 14 | &.type-primary { 15 | background-color: $color_primary; 16 | border: none; 17 | padding: 1.2rem 2rem; 18 | border-radius: 3rem; 19 | transition: all ease 400ms; 20 | cursor: pointer; 21 | &:hover { 22 | background-color: lighten($color_primary, 20%); 23 | } 24 | } 25 | 26 | &.type-secondary { 27 | @extend .type-primary; 28 | background-color: $color_secondary; 29 | 30 | &:hover { 31 | background-color: lighten($color_secondary, 20%); 32 | } 33 | } 34 | 35 | &.type-mute { 36 | @extend .type-primary; 37 | border: 1px solid lighten($color_title, 20%); 38 | background-color: transparent; 39 | color: $color_title; 40 | 41 | &:hover { 42 | background-color: $color_title; 43 | color: $color_background; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /libs/common-layout/src/lib/navbar/navbar.tsx: -------------------------------------------------------------------------------- 1 | import { memo } from 'react'; 2 | 3 | import Image from 'next/image'; 4 | import Link from 'next/link'; 5 | 6 | import { 7 | INavbar, 8 | INavbarProps, 9 | } from '@zlab/shared-models'; 10 | 11 | import styles from './navbar.module.scss'; 12 | 13 | export function Navbar(props: INavbarProps) { 14 | const { navbarItems } = props; 15 | 16 | const renderItem = (item: INavbar) => ( 17 |
  • 18 | 19 | {item.title} 20 | 21 |
  • 22 | ); 23 | 24 | const renderNavbarItems = () => navbarItems?.map((item) => renderItem(item)); 25 | 26 | return ( 27 | 39 | ); 40 | } 41 | 42 | function propsAreEqual(prev: INavbarProps, next: INavbarProps) { 43 | return prev.navbarItems === next.navbarItems; 44 | } 45 | 46 | export default memo(Navbar, propsAreEqual); 47 | -------------------------------------------------------------------------------- /apps/machines-validator-e2e/src/support/commands.ts: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | 11 | // eslint-disable-next-line @typescript-eslint/no-namespace 12 | declare namespace Cypress { 13 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 14 | interface Chainable { 15 | login(email: string, password: string): void; 16 | } 17 | } 18 | // 19 | // -- This is a parent command -- 20 | Cypress.Commands.add('login', (email, password) => { 21 | console.log('Custom command example: Login', email, password); 22 | }); 23 | // 24 | // -- This is a child command -- 25 | // Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) 26 | // 27 | // 28 | // -- This is a dual command -- 29 | // Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) 30 | // 31 | // 32 | // -- This will overwrite an existing command -- 33 | // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) 34 | -------------------------------------------------------------------------------- /libs/common-layout/src/lib/navbar/navbar.module.scss: -------------------------------------------------------------------------------- 1 | @import '/libs/design-system/src/lib/variables.scss'; 2 | @import '/libs/design-system/src/lib/mixins.scss'; 3 | 4 | .navbar { 5 | padding: 1.6rem 0; 6 | background-color: rgba(0, 0, 0, 0.1); 7 | display: flex; 8 | align-items: center; 9 | margin-bottom: 3rem; 10 | 11 | 12 | 13 | :global(.container) { 14 | display: flex; 15 | align-items: center; 16 | justify-content: space-between; 17 | 18 | @include mobile(){ 19 | flex-direction: column; 20 | gap: 1rem; 21 | } 22 | 23 | @include tablet(){ 24 | flex-direction: column; 25 | gap: 1rem; 26 | } 27 | } 28 | } 29 | 30 | .navigation-container { 31 | display: flex; 32 | gap: 2.4rem; 33 | @include mobile(){ 34 | gap: 1.2rem; 35 | width: 100%; 36 | overflow: scroll; 37 | white-space: nowrap; 38 | padding: 2rem 0; 39 | } 40 | 41 | @include tablet(){ 42 | padding: 2rem 0; 43 | } 44 | 45 | a { 46 | color: $color_title; 47 | 48 | @include mobile(){ 49 | color: $color_background; 50 | display: block; 51 | background-color: $color_primary; 52 | border-radius: 10rem; 53 | padding: .5rem 1.2rem; 54 | font-size: 1.3rem; 55 | } 56 | 57 | @include mobile(){ 58 | color: $color_background; 59 | display: block; 60 | background-color: $color_primary; 61 | border-radius: 10rem; 62 | padding: .5rem 1.2rem; 63 | font-size: 1.3rem; 64 | } 65 | 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /apps/machines-validator/styles/global.scss: -------------------------------------------------------------------------------- 1 | @import '/libs/design-system/src/lib/variables.scss'; 2 | 3 | html { 4 | font-size: 10px; 5 | } 6 | body { 7 | font-family: $font_primary; 8 | background-color: $color_background; 9 | color: $color_text; 10 | scroll-behavior: smooth; 11 | overflow-x: hidden; 12 | font-size: 1.6rem; 13 | 14 | &::-webkit-scrollbar { 15 | width: 7px; 16 | } 17 | 18 | /* Track */ 19 | &::-webkit-scrollbar-track { 20 | box-shadow: inset 0 0 5px #f5f5f5; 21 | border-radius: 10px; 22 | } 23 | 24 | /* Handle */ 25 | &::-webkit-scrollbar-thumb { 26 | background: #716e6e; 27 | border-radius: 10px; 28 | } 29 | 30 | /* Handle on hover */ 31 | &::-webkit-scrollbar-thumb:hover { 32 | background: #333; 33 | } 34 | } 35 | 36 | h1, 37 | h2, 38 | h3, 39 | h4, 40 | h5, 41 | h6 { 42 | margin: 1rem 0; 43 | } 44 | 45 | p, 46 | ul, 47 | li { 48 | margin: 0; 49 | } 50 | 51 | li { 52 | list-style: none; 53 | } 54 | 55 | ul { 56 | padding: 0; 57 | } 58 | 59 | * { 60 | box-sizing: border-box; 61 | } 62 | 63 | .container { 64 | width: 100%; 65 | padding-right: 15px; 66 | padding-left: 15px; 67 | margin-right: auto; 68 | margin-left: auto; 69 | } 70 | 71 | // Mobile 72 | @media (max-width: 767px) { 73 | .container { 74 | max-width: 767px; 75 | } 76 | } 77 | 78 | // Tablet 79 | @media (min-width: 768px) and (max-width: 1319) { 80 | .container { 81 | max-width: 1319; 82 | } 83 | } 84 | 85 | // Desktop 86 | @media (min-width: 1320px) { 87 | .container { 88 | max-width: 1320px; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /libs/shared-components/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 3 | "sourceRoot": "libs/shared-components/src", 4 | "projectType": "library", 5 | "tags": [], 6 | "targets": { 7 | "lint": { 8 | "executor": "@nrwl/linter:eslint", 9 | "outputs": ["{options.outputFile}"], 10 | "options": { 11 | "lintFilePatterns": ["libs/shared-components/**/*.{ts,tsx,js,jsx}"] 12 | } 13 | }, 14 | "test": { 15 | "executor": "@nrwl/jest:jest", 16 | "outputs": ["coverage/libs/shared-components"], 17 | "options": { 18 | "jestConfig": "libs/shared-components/jest.config.ts", 19 | "passWithNoTests": true 20 | } 21 | }, 22 | "storybook": { 23 | "executor": "@nrwl/storybook:storybook", 24 | "options": { 25 | "uiFramework": "@storybook/react", 26 | "port": 4400, 27 | "config": { 28 | "configFolder": "libs/shared-components/.storybook" 29 | } 30 | }, 31 | "configurations": { 32 | "ci": { 33 | "quiet": true 34 | } 35 | } 36 | }, 37 | "build-storybook": { 38 | "executor": "@nrwl/storybook:build", 39 | "outputs": ["{options.outputPath}"], 40 | "options": { 41 | "uiFramework": "@storybook/react", 42 | "outputPath": "dist/storybook/shared-components", 43 | "config": { 44 | "configFolder": "libs/shared-components/.storybook" 45 | } 46 | }, 47 | "configurations": { 48 | "ci": { 49 | "quiet": true 50 | } 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /libs/shared-components/src/lib/dropdown/dropdown.module.scss: -------------------------------------------------------------------------------- 1 | @import '/libs/design-system/src/lib/variables.scss'; 2 | @import '/libs/design-system/src/lib/mixins.scss'; 3 | 4 | .container { 5 | position: relative; 6 | background-color: $color_primary; 7 | min-width: 22rem; 8 | border-radius: 10rem; 9 | color: $color_background; 10 | } 11 | 12 | .inner-container { 13 | position: absolute; 14 | background-color: $color_primary; 15 | border-radius: 2rem; 16 | width: 100%; 17 | left: 0; 18 | top: 100%; 19 | margin-top: 0.3rem; 20 | visibility: hidden; 21 | opacity: 0; 22 | transition: all ease 400ms; 23 | z-index: 1000; 24 | 25 | &.active { 26 | visibility: visible; 27 | opacity: 1; 28 | } 29 | } 30 | 31 | .selected-option-container { 32 | padding: 1.2rem 2rem; 33 | cursor: pointer; 34 | } 35 | 36 | .selection-option { 37 | letter-spacing: 0.05rem; 38 | min-height: 1.9rem; 39 | user-select: none; 40 | display: flex; 41 | justify-content: space-between; 42 | align-items: center; 43 | } 44 | 45 | .search-input { 46 | width: calc(100% - 2rem); 47 | border: none; 48 | padding: 1rem 1.5rem; 49 | border-radius: 2rem; 50 | background-color: $color_text; 51 | margin-bottom: 1.5rem; 52 | margin: 1rem; 53 | } 54 | 55 | .options-list { 56 | margin: 1rem; 57 | display: flex; 58 | flex-direction: column; 59 | max-height: 250px; 60 | overflow: auto; 61 | li { 62 | padding: 1rem 0.5rem; 63 | border-radius: 0.5rem; 64 | text-transform: capitalize; 65 | cursor: pointer; 66 | transition: all ease 400ms; 67 | 68 | &:hover { 69 | background-color: $color_text; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /libs/shared-components/src/lib/dropdown/__snapshots__/dropdown.spec.tsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Dropdown should match the snapshot 1`] = ` 4 | 5 |
    6 |
    9 |
    13 |

    16 | Select option 17 |

    18 |
    19 |
    23 |
    24 |
    25 | 26 | `; 27 | 28 | exports[`Dropdown should match the snapshot when its open 1`] = ` 29 | 30 |
    31 |
    34 |
    38 |

    41 | Select option 42 |

    43 |
    44 |
    48 | 54 |
      57 |
    • 58 | Kassulke & Sohn 59 |
    • 60 |
    • 61 | Bayer-Bergnaum 62 |
    • 63 |
    • 64 | Schinner Group 65 |
    • 66 |
    67 |
    68 |
    69 |
    70 | 71 | `; 72 | -------------------------------------------------------------------------------- /apps/machines-validator/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import '../styles/global.scss'; 2 | 3 | import { AppProps } from 'next/app'; 4 | import dynamic from 'next/dynamic'; 5 | import Head from 'next/head'; 6 | 7 | import { 8 | AnchorTagTarget, 9 | INavbar, 10 | INavbarProps, 11 | } from '@zlab/shared-models'; 12 | 13 | const Navbar = dynamic(() => 14 | import('@zlab/common-layout').then((layout) => layout.Navbar) 15 | ); 16 | 17 | interface PageProps extends AppProps { 18 | navbarItems: INavbar[]; 19 | } 20 | 21 | function CustomApp({ Component, pageProps, navbarItems }: PageProps) { 22 | return ( 23 | <> 24 | 25 | zLab Internal Utilities 26 | 27 |
    28 | 29 |
    30 | 31 |
    32 |
    33 | 34 | ); 35 | } 36 | 37 | CustomApp.getInitialProps = () => { 38 | //Todo: Fetch data from server 39 | const navbarItems: INavbar[] = [ 40 | { 41 | id: 1, 42 | title: 'Machines Validator', 43 | href: '/', 44 | target: AnchorTagTarget.SELF, 45 | }, 46 | { 47 | id: 2, 48 | title: 'Service Center', 49 | href: '/service-center', 50 | target: AnchorTagTarget.SELF, 51 | }, 52 | { 53 | id: 3, 54 | title: 'Customers', 55 | href: '/customers', 56 | target: AnchorTagTarget.SELF, 57 | }, 58 | { 59 | id: 4, 60 | title: 'Profile', 61 | href: '/profile', 62 | target: AnchorTagTarget.SELF, 63 | }, 64 | { 65 | id: 5, 66 | title: 'Sign out', 67 | href: '/sign-out', 68 | target: AnchorTagTarget.SELF, 69 | }, 70 | ]; 71 | 72 | return { 73 | navbarItems, 74 | }; 75 | }; 76 | 77 | export default CustomApp; 78 | -------------------------------------------------------------------------------- /apps/machines-validator/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../../node_modules/nx/schemas/project-schema.json", 3 | "sourceRoot": "apps/machines-validator", 4 | "projectType": "application", 5 | "targets": { 6 | "build": { 7 | "executor": "@nrwl/next:build", 8 | "outputs": ["{options.outputPath}"], 9 | "defaultConfiguration": "production", 10 | "options": { 11 | "root": "apps/machines-validator", 12 | "outputPath": "dist/apps/machines-validator" 13 | }, 14 | "configurations": { 15 | "development": {}, 16 | "production": {} 17 | } 18 | }, 19 | "serve": { 20 | "executor": "@nrwl/next:server", 21 | "defaultConfiguration": "development", 22 | "options": { 23 | "buildTarget": "machines-validator:build", 24 | "dev": true 25 | }, 26 | "configurations": { 27 | "development": { 28 | "buildTarget": "machines-validator:build:development", 29 | "dev": true 30 | }, 31 | "production": { 32 | "buildTarget": "machines-validator:build:production", 33 | "dev": false 34 | } 35 | } 36 | }, 37 | "export": { 38 | "executor": "@nrwl/next:export", 39 | "options": { 40 | "buildTarget": "machines-validator:build:production" 41 | } 42 | }, 43 | "test": { 44 | "executor": "@nrwl/jest:jest", 45 | "outputs": ["coverage/apps/machines-validator"], 46 | "options": { 47 | "jestConfig": "apps/machines-validator/jest.config.ts", 48 | "passWithNoTests": true 49 | } 50 | }, 51 | "lint": { 52 | "executor": "@nrwl/linter:eslint", 53 | "outputs": ["{options.outputFile}"], 54 | "options": { 55 | "lintFilePatterns": ["apps/machines-validator/**/*.{ts,tsx,js,jsx}"] 56 | } 57 | } 58 | }, 59 | "tags": [] 60 | } 61 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zlab", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "start": "nx serve", 7 | "build": "nx build", 8 | "test": "nx run-many --all --target=test", 9 | "coverage": "nx run-many --all --target=test --codeCoverage" 10 | }, 11 | "private": true, 12 | "dependencies": { 13 | "@nrwl/next": "14.3.6", 14 | "axios": "^0.27.2", 15 | "core-js": "^3.6.5", 16 | "next": "12.1.6", 17 | "react": "18.2.0", 18 | "react-dom": "18.2.0", 19 | "react-feather": "^2.0.10", 20 | "react-is": "18.2.0", 21 | "regenerator-runtime": "0.13.7", 22 | "styled-components": "5.3.5", 23 | "tslib": "^2.3.0" 24 | }, 25 | "devDependencies": { 26 | "@babel/core": "7.12.13", 27 | "@babel/preset-typescript": "7.12.13", 28 | "@nrwl/cli": "14.3.6", 29 | "@nrwl/cypress": "14.3.6", 30 | "@nrwl/eslint-plugin-nx": "14.3.6", 31 | "@nrwl/jest": "14.3.6", 32 | "@nrwl/linter": "14.3.6", 33 | "@nrwl/react": "14.3.6", 34 | "@nrwl/storybook": "14.3.6", 35 | "@nrwl/web": "14.3.6", 36 | "@nrwl/workspace": "14.3.6", 37 | "@storybook/addon-essentials": "~6.5.9", 38 | "@storybook/builder-webpack5": "~6.5.9", 39 | "@storybook/core-server": "~6.5.9", 40 | "@storybook/manager-webpack5": "~6.5.9", 41 | "@storybook/react": "~6.5.9", 42 | "@svgr/webpack": "^5.4.0", 43 | "@testing-library/jest-dom": "^5.16.4", 44 | "@testing-library/react": "13.3.0", 45 | "@types/jest": "27.4.1", 46 | "@types/node": "16.11.7", 47 | "@types/react": "18.0.13", 48 | "@types/react-dom": "18.0.5", 49 | "@types/react-is": "17.0.3", 50 | "@types/styled-components": "5.1.25", 51 | "@typescript-eslint/eslint-plugin": "~5.24.0", 52 | "@typescript-eslint/parser": "~5.24.0", 53 | "babel-jest": "27.5.1", 54 | "babel-loader": "8.1.0", 55 | "babel-plugin-styled-components": "1.10.7", 56 | "cypress": "^9.1.0", 57 | "eslint": "~8.15.0", 58 | "eslint-config-next": "12.1.6", 59 | "eslint-config-prettier": "8.1.0", 60 | "eslint-plugin-cypress": "^2.10.3", 61 | "eslint-plugin-import": "2.26.0", 62 | "eslint-plugin-jsx-a11y": "6.5.1", 63 | "eslint-plugin-react": "7.30.0", 64 | "eslint-plugin-react-hooks": "4.6.0", 65 | "jest": "27.5.1", 66 | "nx": "14.3.6", 67 | "prettier": "^2.6.2", 68 | "react-test-renderer": "18.2.0", 69 | "sass": "1.52.3", 70 | "ts-jest": "27.1.4", 71 | "ts-node": "~10.8.0", 72 | "typescript": "~4.7.2", 73 | "url-loader": "^3.0.0" 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /libs/common-layout/src/lib/navbar/navbar.spec.tsx: -------------------------------------------------------------------------------- 1 | import { render } from '@testing-library/react'; 2 | import { 3 | AnchorTagTarget, 4 | INavbar, 5 | } from '@zlab/shared-models'; 6 | 7 | import Navbar from './navbar'; 8 | 9 | const dummyItems: INavbar[] = [ 10 | { 11 | id: 1, 12 | title: 'Home', 13 | href: '/', 14 | target: AnchorTagTarget.SELF, 15 | }, 16 | { 17 | id: 2, 18 | title: 'Work Approach', 19 | href: '/work-approach', 20 | target: AnchorTagTarget.SELF, 21 | }, 22 | { 23 | id: 3, 24 | title: 'Jobs', 25 | href: '/jobs', 26 | target: AnchorTagTarget.SELF, 27 | }, 28 | { 29 | id: 4, 30 | title: 'About us', 31 | href: '/about-us', 32 | target: AnchorTagTarget.SELF, 33 | }, 34 | { 35 | id: 5, 36 | title: 'Contact us', 37 | href: '/contact-us', 38 | target: AnchorTagTarget.SELF, 39 | }, 40 | ]; 41 | 42 | describe('Navbar', () => { 43 | it('image should render properly', () => { 44 | const { getByTestId } = render(); 45 | expect('src' in getByTestId('logo-image').attributes).toBeTruthy(); 46 | }); 47 | 48 | it('should render successfully', () => { 49 | const { baseElement } = render(); 50 | expect(baseElement).toBeTruthy(); 51 | }); 52 | 53 | it('should not crash if we provide a zero length data', () => { 54 | const { baseElement } = render(); 55 | expect(baseElement).toBeTruthy(); 56 | }); 57 | 58 | it('should render the exact numbers that we provide', () => { 59 | const expectedLength = dummyItems.length; 60 | const { getAllByRole } = render(); 61 | 62 | expect(getAllByRole('listitem').length).toEqual(expectedLength); 63 | }); 64 | 65 | it('items should have the same href as provided', () => { 66 | const { getAllByRole } = render(); 67 | 68 | const listItems = getAllByRole('link'); 69 | 70 | expect( 71 | listItems.every( 72 | (item, index) => item.getAttribute('href') === dummyItems[index].href 73 | ) 74 | ).toBeTruthy(); 75 | }); 76 | 77 | it('items should have the same target as provided', () => { 78 | const { getAllByRole } = render(); 79 | 80 | const listItems = getAllByRole('link'); 81 | 82 | expect( 83 | listItems.every( 84 | (item, index) => 85 | item.getAttribute('target') === dummyItems[index].target 86 | ) 87 | ).toBeTruthy(); 88 | }); 89 | }); 90 | -------------------------------------------------------------------------------- /libs/shared-components/src/lib/table/table.module.scss: -------------------------------------------------------------------------------- 1 | @import '/libs/design-system/src/lib/variables.scss'; 2 | @import '/libs/design-system/src/lib/mixins.scss'; 3 | 4 | .container { 5 | margin: 4rem 0; 6 | 7 | @include mobile(){ 8 | width: 100%; 9 | overflow: scroll; 10 | } 11 | 12 | @include tablet(){ 13 | width: 100%; 14 | overflow: scroll; 15 | } 16 | 17 | table { 18 | width: 100%; 19 | 20 | @include mobile(){ 21 | td{ 22 | min-width: 200px; 23 | 24 | &:first-of-type{ 25 | min-width: 70px; 26 | } 27 | } 28 | } 29 | 30 | @include tablet(){ 31 | td{ 32 | min-width: 200px; 33 | 34 | &:first-of-type{ 35 | min-width: 70px; 36 | } 37 | } 38 | } 39 | 40 | thead { 41 | tr:first-of-type { 42 | td:first-of-type { 43 | border-top-left-radius: 1rem; 44 | } 45 | td:last-of-type { 46 | border-top-right-radius: 1rem; 47 | } 48 | } 49 | td { 50 | font-weight: bold; 51 | color: $color_primary; 52 | } 53 | } 54 | 55 | tbody { 56 | tr:last-of-type { 57 | td:first-of-type { 58 | border-bottom-left-radius: 10px; 59 | } 60 | td:last-of-type { 61 | border-bottom-right-radius: 10px; 62 | } 63 | } 64 | } 65 | 66 | td { 67 | padding: 1.5rem 0; 68 | text-align: center; 69 | transition: all ease 400ms; 70 | } 71 | 72 | tr:nth-child(odd) td { 73 | background-color: lighten($color_background, 7%); 74 | } 75 | 76 | tr:nth-child(even) td { 77 | background-color: lighten($color_background, 3%); 78 | } 79 | 80 | tr:hover td { 81 | background-color: $color_secondary !important; 82 | } 83 | } 84 | } 85 | 86 | .table-header-item { 87 | display: flex; 88 | gap: 2rem; 89 | justify-content: center; 90 | align-items: center; 91 | 92 | ul { 93 | display: flex; 94 | flex-direction: column; 95 | align-items: center; 96 | justify-content: center; 97 | 98 | li{ 99 | cursor: pointer; 100 | } 101 | } 102 | } 103 | 104 | .state { 105 | padding: 0.4rem 1.2rem; 106 | font-size: 1.3rem; 107 | border-radius: 10rem; 108 | display: inline-block; 109 | 110 | &.true { 111 | background-color: $color_success; 112 | } 113 | 114 | &.false { 115 | background-color: $color_danger; 116 | } 117 | } 118 | 119 | .empty-state{ 120 | height: 200px; 121 | display: flex; 122 | justify-content: center; 123 | align-items: center; 124 | font-size: 2rem; 125 | } -------------------------------------------------------------------------------- /libs/shared-components/src/lib/table/table.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | useEffect, 3 | useState, 4 | } from 'react'; 5 | 6 | import { 7 | ChevronDown, 8 | ChevronUp, 9 | } from 'react-feather'; 10 | 11 | import { ColumnTemplate } from '@zlab/shared-models'; 12 | 13 | import styles from './table.module.scss'; 14 | 15 | export interface TableProps { 16 | columns: ColumnTemplate[]; 17 | data: T[]; 18 | } 19 | 20 | export const Table = ({ columns, data }: TableProps) => { 21 | const [sortedData, setSortedData] = useState(data); 22 | 23 | useEffect(() => { 24 | setSortedData(data); 25 | }, [data]); 26 | 27 | const handleSort = (key: string, order: 'ASC' | 'DESC') => { 28 | const newData = Object.assign([], data); 29 | 30 | if (order === 'ASC') newData.sort((a, b) => (a[key] > b[key] ? 1 : -1)); 31 | if (order === 'DESC') newData.sort((a, b) => (a[key] < b[key] ? 1 : -1)); 32 | 33 | setSortedData(newData); 34 | }; 35 | 36 | const renderBoolean = (state: boolean) => { 37 | if (state) 38 | return ( 39 |
    Active
    40 | ); 41 | 42 | return ( 43 |
    Not Active
    44 | ); 45 | }; 46 | 47 | const renderHeaders = () => 48 | columns.map((r) => ( 49 |
    72 | )); 73 | 74 | const renderRows = () => 75 | [...Array(sortedData.length).keys()].map((row: number) => ( 76 | 77 | {columns.map((r: ColumnTemplate) => ( 78 | 83 | ))} 84 | 85 | )); 86 | 87 | return ( 88 |
    89 |
    50 |
    51 | {r.title} 52 | {r.sortable && ( 53 |
      54 |
    • 55 | handleSort(r.valueKey, 'ASC')} 59 | /> 60 |
    • 61 |
    • 62 | handleSort(r.valueKey, 'DESC')} 66 | /> 67 |
    • 68 |
    69 | )} 70 |
    71 |
    79 | {r.type === 'boolean' && 80 | renderBoolean((sortedData as [])[row][r.valueKey] as boolean)} 81 | {r.type === 'string' && (sortedData as [])[row][r.valueKey]} 82 |
    90 | 91 | {renderHeaders()} 92 | 93 | {data.length > 0 && {renderRows()}} 94 |
    95 | {data.length === 0 && ( 96 |
    No Data!
    97 | )} 98 | 99 | ); 100 | }; 101 | 102 | export default Table; 103 | -------------------------------------------------------------------------------- /libs/shared-components/src/lib/checkbox/checkbox.module.scss: -------------------------------------------------------------------------------- 1 | @import '/libs/design-system/src/lib/variables.scss'; 2 | @import '/libs/design-system/src/lib/mixins.scss'; 3 | 4 | .container { 5 | input[type='checkbox'] { 6 | --active: #ffcf54; 7 | --active-inner: #1b1724; 8 | --focus: 2px transparent; 9 | --border: #ffcf54; 10 | --border-hover: #ffcf54; 11 | --background: transparent; 12 | --disabled: #ffcf54; 13 | --disabled-inner: #e1e6f9; 14 | -webkit-appearance: none; 15 | -moz-appearance: none; 16 | height: 21px; 17 | outline: none; 18 | display: inline-block; 19 | vertical-align: top; 20 | position: relative; 21 | margin: 0; 22 | cursor: pointer; 23 | border: 1px solid var(--bc, var(--border)); 24 | background: var(--b, var(--background)); 25 | transition: background 0.3s, border-color 0.3s, box-shadow 0.2s; 26 | &:after { 27 | content: ''; 28 | display: block; 29 | left: 0; 30 | top: 0; 31 | position: absolute; 32 | transition: transform var(--d-t, 0.3s) var(--d-t-e, ease), 33 | opacity var(--d-o, 0.2s); 34 | } 35 | &:checked { 36 | --b: var(--active); 37 | --bc: var(--active); 38 | --d-o: 0.3s; 39 | --d-t: 0.6s; 40 | --d-t-e: cubic-bezier(0.2, 0.85, 0.32, 1.2); 41 | } 42 | &:disabled { 43 | --b: var(--disabled); 44 | cursor: not-allowed; 45 | opacity: 0.9; 46 | &:checked { 47 | --b: var(--disabled-inner); 48 | --bc: var(--border); 49 | } 50 | & + label { 51 | cursor: not-allowed; 52 | } 53 | } 54 | &:hover { 55 | &:not(:checked) { 56 | &:not(:disabled) { 57 | --bc: var(--border-hover); 58 | } 59 | } 60 | } 61 | &:focus { 62 | box-shadow: 0 0 0 var(--focus); 63 | } 64 | &:not(.switch) { 65 | width: 21px; 66 | &:after { 67 | opacity: var(--o, 0); 68 | } 69 | &:checked { 70 | --o: 1; 71 | } 72 | } 73 | & + label { 74 | font-size: 14px; 75 | line-height: 21px; 76 | display: inline-block; 77 | vertical-align: top; 78 | cursor: pointer; 79 | margin-left: 4px; 80 | } 81 | } 82 | input[type='checkbox'] { 83 | &:not(.switch) { 84 | border-radius: 7px; 85 | &:after { 86 | width: 5px; 87 | height: 9px; 88 | border: 2px solid var(--active-inner); 89 | border-top: 0; 90 | border-left: 0; 91 | left: 7px; 92 | top: 4px; 93 | transform: rotate(var(--r, 20deg)); 94 | } 95 | &:checked { 96 | --r: 43deg; 97 | } 98 | } 99 | &.switch { 100 | width: 38px; 101 | border-radius: 11px; 102 | &:after { 103 | left: 2px; 104 | top: 2px; 105 | border-radius: 50%; 106 | width: 15px; 107 | height: 15px; 108 | background: var(--ab, var(--border)); 109 | transform: translateX(var(--x, 0)); 110 | } 111 | &:checked { 112 | --ab: var(--active-inner); 113 | --x: 17px; 114 | } 115 | &:disabled { 116 | &:not(:checked) { 117 | &:after { 118 | opacity: 0.6; 119 | } 120 | } 121 | } 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /libs/shared-components/src/lib/dropdown/dropdown.spec.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | fireEvent, 3 | render, 4 | } from '@testing-library/react'; 5 | 6 | import Dropdown from './dropdown'; 7 | 8 | const dummyData = [ 9 | { id: 1, label: 'Kassulke & Sohn', value: 'Kassulke & Sohn' }, 10 | { id: 2, label: 'Bayer-Bergnaum', value: 'Bayer-Bergnaum' }, 11 | { id: 3, label: 'Schinner Group', value: 'Schinner Group' }, 12 | ]; 13 | 14 | describe('Dropdown', () => { 15 | it('should render successfully', () => { 16 | const { baseElement } = render( 17 | { 19 | console.log(v); 20 | }} 21 | options={dummyData} 22 | /> 23 | ); 24 | expect(baseElement).toBeTruthy(); 25 | }); 26 | 27 | it('should match the snapshot', () => { 28 | const { baseElement } = render( 29 | { 31 | console.log(v); 32 | }} 33 | options={dummyData} 34 | /> 35 | ); 36 | expect(baseElement).toMatchSnapshot(); 37 | }); 38 | 39 | it('Inner container should not have children at start', () => { 40 | const { getByTestId } = render( 41 | { 43 | console.log(v); 44 | }} 45 | options={dummyData} 46 | /> 47 | ); 48 | 49 | expect(getByTestId('dropdown-inner-container').hasChildNodes()).toBeFalsy(); 50 | }); 51 | 52 | it("Start Selected Option text should be equal to 'Select option'", () => { 53 | const { getByText } = render( 54 | { 56 | console.log(v); 57 | }} 58 | options={dummyData} 59 | /> 60 | ); 61 | const expectingText = 'Select option'; 62 | expect(getByText(expectingText)).toBeTruthy(); 63 | }); 64 | 65 | it('Clicking on dropdown should expose its values', async () => { 66 | const { findByTestId, getByTestId } = render( 67 | { 69 | console.log(v); 70 | }} 71 | options={dummyData} 72 | /> 73 | ); 74 | 75 | fireEvent( 76 | getByTestId('parent-container'), 77 | new MouseEvent('click', { 78 | bubbles: true, 79 | cancelable: true, 80 | }) 81 | ); 82 | 83 | const innerContainer = await findByTestId('dropdown-inner-container'); 84 | expect( 85 | innerContainer.querySelector('ul')?.childElementCount 86 | ).toBeGreaterThan(0); 87 | }); 88 | 89 | it('Selected item should change after clicking on an item', async () => { 90 | const { findByTestId, getByTestId, getByText } = render( 91 | { 93 | console.log(v); 94 | }} 95 | options={dummyData} 96 | /> 97 | ); 98 | 99 | fireEvent( 100 | getByTestId('parent-container'), 101 | new MouseEvent('click', { 102 | bubbles: true, 103 | cancelable: true, 104 | }) 105 | ); 106 | 107 | const firstItemValue = dummyData[0].label; 108 | 109 | const innerContainer = await findByTestId('dropdown-inner-container'); 110 | 111 | fireEvent( 112 | innerContainer.querySelectorAll('ul li')[0], 113 | new MouseEvent('click', { 114 | bubbles: true, 115 | cancelable: true, 116 | }) 117 | ); 118 | 119 | expect(getByText(firstItemValue)).toBeTruthy(); 120 | }); 121 | 122 | it('should match the snapshot when its open', () => { 123 | const { baseElement, getByTestId } = render( 124 | { 126 | console.log(v); 127 | }} 128 | options={dummyData} 129 | /> 130 | ); 131 | 132 | fireEvent( 133 | getByTestId('parent-container'), 134 | new MouseEvent('click', { 135 | bubbles: true, 136 | cancelable: true, 137 | }) 138 | ); 139 | 140 | expect(baseElement).toMatchSnapshot(); 141 | }); 142 | }); 143 | -------------------------------------------------------------------------------- /libs/shared-components/src/lib/dropdown/dropdown.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | ChangeEvent, 3 | useEffect, 4 | useRef, 5 | useState, 6 | } from 'react'; 7 | 8 | import { ChevronDown } from 'react-feather'; 9 | 10 | import { 11 | IDropdownOption, 12 | IDropdownProps, 13 | } from '@zlab/shared-models'; 14 | 15 | import styles from './dropdown.module.scss'; 16 | 17 | export function Dropdown(props: IDropdownProps) { 18 | const { onChange, options, initialValue, minimumSearchLength = 0 } = props; 19 | 20 | const INITIAL_TEXT = 'Select option'; 21 | 22 | const [selectedOption, setSelectedOption] = useState( 23 | initialValue ? initialValue : INITIAL_TEXT 24 | ); 25 | const [optionsList, setOptionsList] = useState([]); 26 | const [searchTerm, setSearchTerm] = useState(''); 27 | const [listOpenState, setListOpenState] = useState(false); 28 | 29 | const SearchInputRef = useRef(null); 30 | const ContainerRef = useRef(null); 31 | 32 | useEffect(() => { 33 | if (initialValue) { 34 | setSelectedOption(initialValue); 35 | } else { 36 | setSelectedOption(INITIAL_TEXT); 37 | } 38 | }, [initialValue]); 39 | 40 | const handleFillingOptions = () => { 41 | setOptionsList(options); 42 | }; 43 | 44 | const toggleListState = () => { 45 | setListOpenState(!listOpenState); 46 | }; 47 | 48 | const handleSelectOption = (option: IDropdownOption) => { 49 | onChange(option.value); 50 | setSelectedOption(option.label); 51 | toggleListState(); 52 | }; 53 | 54 | const handleSearch = (term: string) => { 55 | if (term.length === 0) setOptionsList(options); 56 | 57 | if (term.length <= minimumSearchLength) return; 58 | 59 | const filteredList = optionsList.filter( 60 | (item) => item.value.toLowerCase().indexOf(term.toLowerCase()) > -1 61 | ); 62 | setOptionsList(filteredList); 63 | }; 64 | 65 | const handleInputFocus = (state: boolean) => { 66 | if (state) { 67 | // we use timeout because the state open have some animation that took 400ms 68 | setTimeout(() => { 69 | SearchInputRef?.current?.focus(); 70 | }, 100); 71 | } 72 | }; 73 | 74 | const handleOutSideClick = () => { 75 | const onClickOutside = (e: any) => { 76 | if (ContainerRef.current && !ContainerRef.current.contains(e.target)) 77 | setListOpenState(false); 78 | }; 79 | 80 | document.addEventListener('click', onClickOutside); 81 | 82 | return () => { 83 | document.removeEventListener('click', onClickOutside); 84 | }; 85 | }; 86 | 87 | useEffect(handleOutSideClick, []); 88 | 89 | useEffect(() => { 90 | handleInputFocus(listOpenState); 91 | }, [listOpenState]); 92 | useEffect(handleFillingOptions, [options]); 93 | useEffect(() => { 94 | handleSearch(searchTerm); 95 | }, [searchTerm]); 96 | 97 | const handleSearchInputChange = (event: ChangeEvent) => { 98 | const value = event.target.value; 99 | setSearchTerm(value.trim()); 100 | }; 101 | 102 | const renderOptions = () => 103 | optionsList.map((option: IDropdownOption) => ( 104 |
  • handleSelectOption(option)}> 105 | {option.label} 106 |
  • 107 | )); 108 | 109 | return ( 110 |
    111 |
    116 |

    117 | {selectedOption} 118 | 119 |

    120 |
    121 |
    127 | {listOpenState && ( 128 | <> 129 | 137 |
      {renderOptions()}
    138 | 139 | )} 140 |
    141 |
    142 | ); 143 | } 144 | 145 | export default Dropdown; 146 | -------------------------------------------------------------------------------- /apps/machines-validator/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | FC, 3 | useState, 4 | } from 'react'; 5 | 6 | import axios from 'axios'; 7 | import { Filter } from 'react-feather'; 8 | 9 | import { 10 | FormContainer, 11 | FormField, 12 | } from '@zlab/common-layout'; 13 | import { 14 | Button, 15 | Checkbox, 16 | Dropdown, 17 | Table, 18 | } from '@zlab/shared-components'; 19 | import { 20 | ColumnTemplate, 21 | ICustomer, 22 | IDropdownOption, 23 | IFilters, 24 | } from '@zlab/shared-models'; 25 | import { 26 | filterCustomers, 27 | generateOptionsByKey, 28 | } from '@zlab/shared-utilities'; 29 | 30 | const tableHeaders: ColumnTemplate[] = [ 31 | { 32 | title: 'ID', 33 | width: '5%', 34 | type: 'string', 35 | valueKey: 'id', 36 | sortable: true, 37 | }, 38 | { 39 | title: 'Customer', 40 | width: '20%', 41 | type: 'string', 42 | valueKey: 'customer', 43 | sortable: false, 44 | }, 45 | { 46 | title: 'Serial Number', 47 | width: '20%', 48 | type: 'string', 49 | valueKey: 'serial_number', 50 | sortable: false, 51 | }, 52 | { 53 | title: 'Asset Type', 54 | width: '20%', 55 | type: 'string', 56 | valueKey: 'asset_type', 57 | sortable: true, 58 | }, 59 | { 60 | title: 'Service Contract Status', 61 | width: '20%', 62 | type: 'boolean', 63 | valueKey: 'service_contract', 64 | sortable: true, 65 | }, 66 | { 67 | title: 'Warranty Status', 68 | width: '20%', 69 | type: 'boolean', 70 | valueKey: 'warranty', 71 | sortable: true, 72 | }, 73 | ]; 74 | 75 | interface Props { 76 | customers: ICustomer[]; 77 | assetTypesList: IDropdownOption[]; 78 | customersList: IDropdownOption[]; 79 | serialNumbersList: IDropdownOption[]; 80 | } 81 | 82 | const Index: FC = ({ customers, assetTypesList, customersList, serialNumbersList }) => { 83 | const [filteredData, setFilteredData] = useState(customers); 84 | 85 | const [filters, setFilters] = useState(); 86 | 87 | const handleFiltersChange = ( 88 | key: keyof IFilters, 89 | newValue: string | boolean 90 | ) => { 91 | const newFilters = Object.assign({}, filters); 92 | newFilters[key as string] = newValue; 93 | setFilters(newFilters); 94 | }; 95 | 96 | const handleApplyFilter = () => { 97 | const filteredCustomers = filterCustomers(customers, filters); 98 | setFilteredData(filteredCustomers); 99 | }; 100 | 101 | const handleResetFilters = () => { 102 | setFilters(null); 103 | setFilteredData(customers); 104 | }; 105 | 106 | return ( 107 |
    108 |

    Customers Overview

    109 |
    110 | 111 | 112 | { 115 | handleFiltersChange('serial_number', v); 116 | }} 117 | options={serialNumbersList} 118 | /> 119 | 120 | 121 | { 125 | handleFiltersChange('customer', v); 126 | }} 127 | options={customersList} 128 | /> 129 | 130 | 131 | { 135 | handleFiltersChange('asset_type', v); 136 | }} 137 | options={assetTypesList} 138 | /> 139 | 140 | 141 | 142 | handleFiltersChange('warranty', v)} 145 | id="Warranty-Status" 146 | title="" 147 | /> 148 | 149 | 150 | 151 | handleFiltersChange('service_contract', v)} 154 | id="Service-Contract-Status" 155 | title="" 156 | /> 157 | 158 | 159 | 160 | 164 | 165 | 166 | 169 | 170 | 171 | 172 | 173 | 174 | ); 175 | }; 176 | 177 | export async function getServerSideProps(context) { 178 | const { data } = await axios.get( 179 | 'https://api.jsonbin.io/v3/b/62b9da24192a674d291c921b' 180 | ); 181 | 182 | const records = data.record as ICustomer[]; 183 | 184 | const assetTypesList = generateOptionsByKey(records, 'asset_type'); 185 | const customersList = generateOptionsByKey(records, 'customer'); 186 | const serialNumbersList = generateOptionsByKey(records, 'serial_number'); 187 | 188 | return { 189 | props: { 190 | customers: records, 191 | assetTypesList, 192 | customersList, 193 | serialNumbersList, 194 | }, 195 | }; 196 | } 197 | 198 | export default Index; 199 | --------------------------------------------------------------------------------