commit 13ab39d1fd6b6ac1e1fa205ad2a4478c88c19c03 Author: Roman Jaroš Date: Sat Sep 9 16:55:38 2023 +0200 Initial builded version Change-Id: I8937a5b559134d1d6029d16e9831c961287fee0c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ce658b9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +build/ +node_modules/ + +.pnpm/ +.pnpm-store/ \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..9cb550f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "window.title": "${activeEditorShort}${separator}Seedling" +} \ No newline at end of file diff --git a/create.js b/create.js new file mode 100644 index 0000000..a82e99d --- /dev/null +++ b/create.js @@ -0,0 +1,37 @@ +const fs = require('fs') +var readlineSync = require('readline-sync'); +const replaceInFiles = require('replace-in-file'); +const argv = require('minimist')(process.argv.slice(2)); + +// questions +const appName = readlineSync.question('What is app name? ', { defaultInput: 'many' }); +const appPort = readlineSync.question('Which port use for deployment? ', { defaultInput: '93' }); +const isMonorepo = readlineSync.keyInYN('Apply monorepo?'); + +// context directory +const contextDir = `${argv._[0]}/` ?? './'; + +// create app folder +const appsDir = `${contextDir}/apps` +const appDir = isMonorepo ? `${contextDir}/apps/${appName}-fe` : `${contextDir}/` +if (!fs.existsSync(appDir)){ + if (isMonorepo) { + fs.mkdirSync(appsDir); + } + fs.mkdirSync(appDir); + fs.mkdirSync(`${appDir}/src`); +} + +// copy folder content +try { + fs.cpSync('./source/shared/', contextDir, { overwrite: false, recursive: true }) + fs.cpSync('./source/app/', appDir, { overwrite: true, recursive: true }) +} catch (err) { + console.error(err) +} + +// replace in files +replaceInFiles.sync({ files: './build/**/*.{ts,tsx}', from: /\/\/.\@ts\-nocheck\n/gm, to: '' }); +replaceInFiles.sync({ files: './build/**/*', from: /\$\(appName\)/gm, to: appName }); +replaceInFiles.sync({ files: './build/**/*', from: /\$\(AppName\)/gm, to: appName.charAt(0).toUpperCase() + appName.slice(1) }); +replaceInFiles.sync({ files: './build/**/*', from: /\$\(appPort\)/gm, to: appPort }); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..4a1ff9e --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "seedling", + "version": "0.1.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "dependencies": { + "minimist": "1.2.8", + "readline-sync": "1.4.10", + "replace-in-file": "7.0.1" + }, + "keywords": [], + "author": "Roman Jaroš", + "license": "ISC" +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..a7e0ac5 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,214 @@ +lockfileVersion: '6.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +dependencies: + minimist: + specifier: 1.2.8 + version: 1.2.8 + readline-sync: + specifier: 1.4.10 + version: 1.4.10 + replace-in-file: + specifier: 7.0.1 + version: 7.0.1 + +packages: + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: false + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: false + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: false + + /brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + dependencies: + balanced-match: 1.0.2 + dev: false + + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: false + + /cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + dev: false + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: false + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: false + + /emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + dev: false + + /escalade@3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: false + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: false + + /get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + dev: false + + /glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 5.1.6 + once: 1.4.0 + dev: false + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: false + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: false + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: false + + /is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + dev: false + + /minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + dependencies: + brace-expansion: 2.0.1 + dev: false + + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: false + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: false + + /readline-sync@1.4.10: + resolution: {integrity: sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw==} + engines: {node: '>= 0.8.0'} + dev: false + + /replace-in-file@7.0.1: + resolution: {integrity: sha512-KbhgPq04eA+TxXuUxpgWIH9k/TjF+28ofon2PXP7vq6izAILhxOtksCVcLuuQLtyjouBaPdlH6RJYYcSPVxCOA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + chalk: 4.1.2 + glob: 8.1.0 + yargs: 17.7.2 + dev: false + + /require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + dev: false + + /string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + dev: false + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: false + + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: false + + /wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: false + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: false + + /y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + dev: false + + /yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + dev: false + + /yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + dependencies: + cliui: 8.0.1 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + dev: false diff --git a/source/app/.eslintignore b/source/app/.eslintignore new file mode 100644 index 0000000..6de2713 --- /dev/null +++ b/source/app/.eslintignore @@ -0,0 +1 @@ +src/api/generated/*.ts \ No newline at end of file diff --git a/source/app/.eslintrc b/source/app/.eslintrc new file mode 100644 index 0000000..3cce05b --- /dev/null +++ b/source/app/.eslintrc @@ -0,0 +1,119 @@ +{ + "parser": "@typescript-eslint/parser", + "extends": [ + "eslint:recommended", + "plugin:react/recommended", + "plugin:@typescript-eslint/recommended", + "prettier" + ], + "plugins": [ + "react", + "react-hooks", + "typescript", + "@typescript-eslint", + "typescript-sort-keys", + "simple-import-sort", + "import" + ], + "env": { + "browser": true, + "node": true, + "es6": true, + "jest": true + }, + "settings": { + "react": { + "version": "detect" + }, + "import/parsers": { + "@typescript-eslint/parser": [ + ".ts", + ".tsx" + ] + } + }, + "overrides": [ + { + "files": [ + "*.js" + ], + "parser": "esprima", + "rules": { + "@typescript-eslint/no-var-requires": 0 + } + } + ], + "rules": { + "max-len": "off", + "no-useless-escape": "off", + "object-curly-spacing": [ + "error", + "always" + ], + "no-multi-spaces": "error", + "no-console": [ + "error", + { + "allow": [ + "info", + "warn", + "error" + ] + } + ], + "no-unused-vars": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/no-unused-vars": 2, + "@typescript-eslint/interface-name-prefix": 0, + "@typescript-eslint/no-empty-interface:": 0, + "@typescript-eslint/explicit-function-return-type": 0, + "@typescript-eslint/explicit-member-accessibility": 2, + "@typescript-eslint/no-explicit-any": 0, + "@typescript-eslint/no-empty-function": 0, + "typescript-sort-keys/interface": "error", + "typescript-sort-keys/string-enum": "error", + "react-hooks/rules-of-hooks": "error", + "react/jsx-tag-spacing": [ + "error", + { + "beforeSelfClosing": "always" + } + ], + "react/prop-types": 0, + "react/jsx-no-bind": 0, + "sort-imports": "off", + "import/no-duplicates": "error", + "import/order": "off", + "simple-import-sort/imports": [ + "error", + { + "groups": [ + [ + "^\\u0000" + ], + [ + "^[^.]" + ], + [ + "^@prokyon?\\w" + ], + [ + "^api?\\w", + "^app?\\w", + "^components?\\w", + "^constants?\\w", + "^features?\\w", + "^hooks?\\w", + "^localization?\\w", + "^pages?\\w", + "^utils?\\w", + "^types?\\w" + ], + [ + "^\\." + ] + ] + } + ] + } +} \ No newline at end of file diff --git a/source/app/README.md b/source/app/README.md new file mode 100644 index 0000000..91f234d --- /dev/null +++ b/source/app/README.md @@ -0,0 +1 @@ +# $(AppName) \ No newline at end of file diff --git a/source/app/config/local/.env b/source/app/config/local/.env new file mode 100644 index 0000000..18cc95a --- /dev/null +++ b/source/app/config/local/.env @@ -0,0 +1 @@ +ENDPOINT_BASE_URL="" \ No newline at end of file diff --git a/source/app/config/prod/.env b/source/app/config/prod/.env new file mode 100644 index 0000000..18cc95a --- /dev/null +++ b/source/app/config/prod/.env @@ -0,0 +1 @@ +ENDPOINT_BASE_URL="" \ No newline at end of file diff --git a/source/app/docker/Dockerfile b/source/app/docker/Dockerfile new file mode 100644 index 0000000..d5f1a0e --- /dev/null +++ b/source/app/docker/Dockerfile @@ -0,0 +1,12 @@ +FROM nginx:1.13.9-alpine + +RUN mkdir app +RUN mkdir -p /run/nginx + +COPY build /app +COPY docker/nginx/conf.d/ /etc/nginx/conf.d/ + +EXPOSE 80 + +VOLUME [ "/etc/nginx/conf.d" ] +ENTRYPOINT ["nginx", "-g", "daemon off;"] diff --git a/source/app/docker/nginx/conf.d/default.conf b/source/app/docker/nginx/conf.d/default.conf new file mode 100644 index 0000000..68ea2a3 --- /dev/null +++ b/source/app/docker/nginx/conf.d/default.conf @@ -0,0 +1,13 @@ +server { + listen 80 default_server; + listen [::]:80 default_server; + + server_name _; + + root /app; + index index.html; + + location / { + try_files $uri $uri/ /index.html; + } +} diff --git a/source/app/jest.config.js b/source/app/jest.config.js new file mode 100644 index 0000000..e0d65e0 --- /dev/null +++ b/source/app/jest.config.js @@ -0,0 +1,21 @@ +module.exports = { + transform: { + '^.+\\.(ts|tsx)$': 'ts-jest', + }, + testRegex: '(/__tests__/.*|(-|/)(test))\\.(ts|tsx)?$', + transformIgnorePatterns: ['/node_modules/.*'], + testPathIgnorePatterns: ['.vscode'], + moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'], + moduleNameMapper: { + '^app(.*)$': '/src/app/$1', + '^api(.*)$': '/src/api/$1', + '^components(.*)$': '/src/components/$1', + '^constants(.*)$': '/src/constants/$1', + '^features(.*)$': '/src/features/$1', + '^hooks(.*)$': '/src/hooks/$1', + '^localization(.*)$': '/src/localization/$1', + '^pages(.*)$': '/src/pages/$1', + '^types(.*)$': '/src/types/$1', + '^utils(.*)$': '/src/utils/$1', + }, +}; diff --git a/source/app/nightwatch.js b/source/app/nightwatch.js new file mode 100644 index 0000000..82c26f0 --- /dev/null +++ b/source/app/nightwatch.js @@ -0,0 +1,41 @@ +/** @type {import('nightwatch').NightwatchOptions} */ + +module.exports = { + src_folders: 'tests/.nightwatchjs/src', + output_folder: 'tests/.nightwatchjs/output', + page_objects_path: ['tests/.nightwatchjs/objects'], + custom_commands_path: 'tests/.nightwatchjs/commands', + use_xpath: true, + end_session_on_fail: true, + start_process: false, + test_settings: { + localhost: { + launch_url: '', + selenium_port: 4444, + selenium_host: '10.2.0.4', + screenshots: { + enabled: true, + on_failure: true, + on_error: true, + path: 'tests/.nightwatchjs/output', + }, + desiredCapabilities: { + browserName: 'chrome', + chromeOptions: { + args: ['no-sandbox'], + }, + }, + }, + ci: { + launch_url: '', + selenium_port: 4444, + selenium_host: '10.0.1.21', + desiredCapabilities: { + browserName: 'chrome', + chromeOptions: { + args: ['headless', 'no-sandbox'], + }, + }, + }, + }, +}; diff --git a/source/app/openapi-config.ts b/source/app/openapi-config.ts new file mode 100644 index 0000000..38020c3 --- /dev/null +++ b/source/app/openapi-config.ts @@ -0,0 +1,14 @@ +// @ts-nocheck +import type { ConfigFile } from '@rtk-query/codegen-openapi'; + +const config: ConfigFile = { + schemaFile: '', + apiFile: './src/api/emptyApi.ts', + apiImport: 'emptyApi', + hooks: { lazyQueries: true, mutations: true, queries: true }, + tag: false, + outputFiles: { + }, +}; + +export default config; diff --git a/source/app/package.json b/source/app/package.json new file mode 100644 index 0000000..41f7ebf --- /dev/null +++ b/source/app/package.json @@ -0,0 +1,95 @@ +{ + "name": "$(appName)-fe", + "version": "0.1.0", + "author": "Roman Jaroš", + "license": "ISC", + "scripts": { + "ci:build": "pnpm build:prod", + "ci:build:test": "pnpm build:test", + "ci:test:e2e": "nightwatch --env ci", + "dev": "webpack serve --config scripts/webpack-dev.js --env config=local", + "lint": "eslint -c .eslintrc src/**/*.{ts,tsx}", + "lint:css": "stylelint src/**/*.ts", + "test": "jest", + "test:watch": "jest --watch", + "test:update": "jest -u", + "test:e2e-build": "tsc -p tests/tsconfig.json -w", + "test:e2e": "nightwatch --env localhost", + "build:prod": "webpack --config scripts/webpack-prod.js", + "build:test": "webpack --config scripts/webpack-dev.js --env config=test", + "serve": "http-server-spa build index.html 3331", + "codegen": "pnpm dlx @rtk-query/codegen-openapi openapi-config.ts" + }, + "dependencies": { + "@prokyon/api": "^1.0.31", + "@prokyon/auth": "^1.0.31", + "@prokyon/components": "^1.0.31", + "@prokyon/constants": "^1.0.31", + "@prokyon/forms": "^1.0.31", + "@prokyon/localization": "^1.0.31", + "@prokyon/styles": "^1.0.31", + "@prokyon/types": "^1.0.31", + "@prokyon/utils": "^1.0.31", + "@reduxjs/toolkit": "1.8.3", + "clsx": "^1.2.1", + "date-fns": "2.29.2", + "history": "5.3.0", + "ramda": "0.28.0", + "react": "17.0.2", + "react-dom": "17.0.2", + "react-redux": "8.0.2", + "typescript": "5.0.4", + "wouter": "2.11.0", + "yup": "0.32.11" + }, + "devDependencies": { + "@rtk-query/codegen-openapi": "1.0.0", + "@types/enzyme": "3.10.11", + "@types/jest": "27.4.1", + "@types/nightwatch": "2.3.24", + "@types/ramda": "0.28.13", + "@types/react": "17.0.43", + "@types/react-dom": "17.0.14", + "@types/react-redux": "7.1.24", + "@types/yup": "0.29.13", + "@typescript-eslint/eslint-plugin": "5.16.0", + "@typescript-eslint/parser": "5.16.0", + "autoprefixer": "10.4.4", + "case-sensitive-paths-webpack-plugin": "2.4.0", + "clean-webpack-plugin": "^4.0.0", + "copy-webpack-plugin": "^10.2.4", + "css-loader": "6.7.1", + "process": "0.11.10", + "dotenv": "16.3.1", + "eslint": "8.11.0", + "eslint-config-prettier": "8.5.0", + "eslint-plugin-import": "2.25.4", + "eslint-plugin-react": "7.29.4", + "eslint-plugin-react-hooks": "4.3.0", + "eslint-plugin-simple-import-sort": "7.0.0", + "eslint-plugin-typescript": "0.14.0", + "eslint-plugin-typescript-sort-keys": "2.1.0", + "file-loader": "6.2.0", + "html-webpack-plugin": "5.5.0", + "http-server-spa": "1.3.0", + "jest": "27.5.1", + "mini-css-extract-plugin": "2.6.0", + "nightwatch": "3.0.1", + "postcss": "8.4.21", + "postcss-loader": "6.2.1", + "prettier": "2.6.1", + "tailwindcss": "3.0.23", + "ts-node": "10.9.1", + "ts-jest": "27.1.4", + "ts-loader": "9.2.8", + "ts-prune": "^0.10.3", + "tsconfig-paths": "3.14.1", + "url-loader": "4.1.1", + "webpack": "5.70.0", + "webpack-bundle-analyzer": "^4.5.0", + "webpack-cli": "4.10.0", + "webpack-dev-server": "4.7.4", + "webpack-merge": "^5.8.0" + }, + "peerDependencies": {} +} \ No newline at end of file diff --git a/source/app/scripts/webpack-common.js b/source/app/scripts/webpack-common.js new file mode 100644 index 0000000..bc191e0 --- /dev/null +++ b/source/app/scripts/webpack-common.js @@ -0,0 +1,82 @@ +require('ts-node/register'); + +const path = require('path'); +const webpack = require('webpack'); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); +const HtmlWebPackPlugin = require('html-webpack-plugin'); +const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); +const CopyPlugin = require('copy-webpack-plugin'); + +const __basedir = path.resolve(__dirname, '..'); + +module.exports = (env, args, myEnv) => ({ + entry: { + app: [path.resolve(__basedir, 'src/index.tsx')], + style: [path.resolve(__basedir, 'src/styles/global.css')], + }, + output: { + path: path.resolve(__basedir, 'build'), + publicPath: '/', + }, + resolve: { + alias: { + api: path.resolve(__basedir, 'src/api'), + app: path.resolve(__basedir, 'src/app'), + components: path.resolve(__basedir, 'src/components'), + constants: path.resolve(__basedir, 'src/constants'), + features: path.resolve(__basedir, 'src/features'), + hooks: path.resolve(__basedir, 'src/hooks'), + localization: path.resolve(__basedir, 'src/localization'), + pages: path.resolve(__basedir, 'src/pages'), + utils: path.resolve(__basedir, 'src/utils'), + types: path.resolve(__basedir, 'src/types'), + }, + extensions: ['.ts', '.tsx', '.js', '.json'], + }, + module: { + rules: [ + { + test: /\.tsx?$/, + loader: 'ts-loader', + }, + { + test: /\.css$/i, + use: [ + MiniCssExtractPlugin.loader, + 'css-loader', + { + loader: 'postcss-loader', + options: { + postcssOptions: { + config: path.resolve(__basedir, 'src/styles/postcss.config.js'), + }, + }, + }, + ], + }, + ], + }, + plugins: [ + new MiniCssExtractPlugin({ + filename: '[name].css', + }), + new HtmlWebPackPlugin({ + hash: true, + filename: 'index.html', //target html + template: path.resolve(__basedir, 'src/app/assets/html/index.ejs'), //source html + env: { + websiteId: process.env.WA_WEBSITE_ID, + }, + }), + new CopyPlugin({ + patterns: [ + { from: path.resolve(__basedir, 'src/app/assets/public'), to: './' }, + ], + }), + new webpack.EnvironmentPlugin(myEnv), + new webpack.ProvidePlugin({ + process: 'process/browser', + }), + new CaseSensitivePathsPlugin(), + ], +}); diff --git a/source/app/scripts/webpack-dev.js b/source/app/scripts/webpack-dev.js new file mode 100644 index 0000000..6797b2d --- /dev/null +++ b/source/app/scripts/webpack-dev.js @@ -0,0 +1,17 @@ +const { merge } = require('webpack-merge'); + +const common = require('./webpack-common'); +const myEnv = require('dotenv').config({ path: 'config/local/.env' }).parsed; + +module.exports = (env, args) => { + return merge(common(env, args, myEnv), { + mode: 'development', + devtool: 'inline-source-map', + devServer: { + host: '0.0.0.0', + port: '3330', + hot: true, + historyApiFallback: true, + }, + }); +}; diff --git a/source/app/scripts/webpack-prod.js b/source/app/scripts/webpack-prod.js new file mode 100644 index 0000000..f567085 --- /dev/null +++ b/source/app/scripts/webpack-prod.js @@ -0,0 +1,16 @@ +const { merge } = require('webpack-merge'); + +const myEnv = require('dotenv').config({ path: 'config/prod/.env' }).parsed; +const common = require('./webpack-common'); + +// const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer'); + +module.exports = (env, args) => { + return merge(common(env, args, myEnv), { + mode: 'production', + devtool: false, + plugins: [ + // new BundleAnalyzerPlugin(), + ], + }); +}; diff --git a/source/app/src/api/emptyApi.ts b/source/app/src/api/emptyApi.ts new file mode 100644 index 0000000..f19645f --- /dev/null +++ b/source/app/src/api/emptyApi.ts @@ -0,0 +1,10 @@ +// @ts-nocheck +import { createApi } from '@reduxjs/toolkit/query/react'; + +import { baseQuery } from '@prokyon/api/query'; + +export const emptyApi = createApi({ + baseQuery: baseQuery({ baseUrl: process.env.ENDPOINT_URL as string }), + tagTypes: [], + endpoints: () => ({}), +}); diff --git a/source/app/src/app/app.tsx b/source/app/src/app/app.tsx new file mode 100644 index 0000000..49fb2cb --- /dev/null +++ b/source/app/src/app/app.tsx @@ -0,0 +1,63 @@ +// @ts-nocheck +import React, { FC, useEffect } from 'react'; +import { Route, Switch, useLocation } from 'wouter'; + +import { useAuth } from '@prokyon/auth/hook/useAuth'; +import { Skeleton } from '@prokyon/components/Skeleton'; +import { MenuItem } from '@prokyon/components/Skeleton/types'; + +import { WelcomePage } from 'pages/WelcomePage'; + +import { buildRoute, Routes } from './routes'; + +const topMenu: MenuItem[][] = [ + [ + { label: 'Routa', href: buildRoute('root') }, + ], + [ + ], +]; + +const userMenu: MenuItem[][] = [ + [ + // public + ], + [ + // authenticated + ], +]; + +const publicSites: string[] = []; + +export const App: FC = () => { + const { authenticated } = useAuth(); + + const [location] = useLocation(); + + const isPublicView = !!publicSites.find((url) => location.startsWith(url)); + const isWelcomePage = window.location.pathname === '/'; + + if (authenticated === null || authenticated === undefined) { + return null; + } + + return ( + $(AppName), + top: authenticated ? topMenu[1] : isWelcomePage ? [] : topMenu[0], + user: authenticated ? userMenu[1] : isPublicView ? [] : userMenu[0], + }}> + {authenticated ? ( + + 404! + + ) : ( + + + 404! + + )} + + ); +}; diff --git a/source/app/src/app/assets/html/index.ejs b/source/app/src/app/assets/html/index.ejs new file mode 100644 index 0000000..7aec956 --- /dev/null +++ b/source/app/src/app/assets/html/index.ejs @@ -0,0 +1,41 @@ + + + + + + + + $(AppName) + + + + + + +
+ + +<% if (htmlWebpackPlugin.options.env.websiteId) { %> + + <% } %> + + \ No newline at end of file diff --git a/source/app/src/app/assets/public/.gitkeep b/source/app/src/app/assets/public/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/source/app/src/app/routes.ts b/source/app/src/app/routes.ts new file mode 100644 index 0000000..ee85777 --- /dev/null +++ b/source/app/src/app/routes.ts @@ -0,0 +1,16 @@ +// @ts-nocheck +import { DotNestedKeys } from '@prokyon/localization/types'; + +export const Routes = { + root: '/' +}; + +export const buildRoute = (route: DotNestedKeys) => { + let path = ''; + let obj: Record = Routes; + route.split('.').forEach((key: any) => { + path += obj[key]?.base ?? obj[key]; + obj = obj[key]; + }); + return path; +}; diff --git a/source/app/src/app/store.ts b/source/app/src/app/store.ts new file mode 100644 index 0000000..2ddf158 --- /dev/null +++ b/source/app/src/app/store.ts @@ -0,0 +1,34 @@ +// @ts-nocheck +// import generated apis + +import { AnyAction, combineReducers, configureStore, ThunkDispatch } from '@reduxjs/toolkit'; +import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'; + +import authReducer from '@prokyon/auth/slice'; +import { AUTH_REDUCER_NAME } from '@prokyon/auth/types'; +import { ROOT_REDUCER_NAME } from '@prokyon/constants/redux'; + +import { emptyApi } from '../api/emptyApi'; + +type prokyonReducer = { + [AUTH_REDUCER_NAME]: typeof authReducer; +}; + +const store = configureStore({ + reducer: { + [ROOT_REDUCER_NAME]: combineReducers({ + [AUTH_REDUCER_NAME]: authReducer, + }), + [emptyApi.reducerPath]: emptyApi.reducer, + }, + middleware: (gDM) => [...gDM({ serializableCheck: false }).concat([emptyApi.middleware])], + devTools: process.env.NODE_ENV === 'development' +}); + +export type RootState = ReturnType; +export type AppDispatch = ThunkDispatch; + +export const useAppDispatch: () => AppDispatch = useDispatch; +export const useAppSelector: TypedUseSelectorHook = useSelector; + +export default store; diff --git a/source/app/src/components/.gitkeep b/source/app/src/components/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/source/app/src/constants/.gitkeep b/source/app/src/constants/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/source/app/src/features/.gitkeep b/source/app/src/features/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/source/app/src/hooks/.gitkeep b/source/app/src/hooks/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/source/app/src/index.tsx b/source/app/src/index.tsx new file mode 100644 index 0000000..dc454d5 --- /dev/null +++ b/source/app/src/index.tsx @@ -0,0 +1,25 @@ +// @ts-nocheck +import 'types/global'; +import 'localization/locale'; +import 'utils/yup'; + +import React from 'react'; +import { render } from 'react-dom'; +import { Provider } from 'react-redux'; + +import { ModalWrapper } from '@prokyon/components/Modal/context'; +import { ToasterWrapper } from '@prokyon/components/Toaster/context'; + +import { App } from './app/app'; +import store from './app/store'; + +render( + + + + + + + , + document.getElementById('app') +); diff --git a/source/app/src/localization/dictionary/csCZ.ts b/source/app/src/localization/dictionary/csCZ.ts new file mode 100644 index 0000000..8862530 --- /dev/null +++ b/source/app/src/localization/dictionary/csCZ.ts @@ -0,0 +1,5 @@ +// @ts-nocheck +import { Dictionary } from 'localization/schema'; + +export const csCZ: Dictionary = { +}; diff --git a/source/app/src/localization/locale.ts b/source/app/src/localization/locale.ts new file mode 100644 index 0000000..b58725f --- /dev/null +++ b/source/app/src/localization/locale.ts @@ -0,0 +1,7 @@ +// @ts-nocheck +import { lang } from '@prokyon/localization/init'; + +import { csCZ } from './dictionary/csCZ'; + +lang.addResource('cs', csCZ); +lang.setLocale('cs'); diff --git a/source/app/src/localization/message.ts b/source/app/src/localization/message.ts new file mode 100644 index 0000000..1376aa7 --- /dev/null +++ b/source/app/src/localization/message.ts @@ -0,0 +1,11 @@ +// @ts-nocheck +import { t as pT } from '@prokyon/localization/message'; +import { Dictionary as prokyonDictionary, DotNestedKeys } from '@prokyon/localization/types'; + +import { Dictionary } from './schema'; + +type FullDictionary = prokyonDictionary & Dictionary; + +export function t(key: DotNestedKeys, variables?: Record) { + return pT(key, variables); +} \ No newline at end of file diff --git a/source/app/src/localization/schema.ts b/source/app/src/localization/schema.ts new file mode 100644 index 0000000..7a61509 --- /dev/null +++ b/source/app/src/localization/schema.ts @@ -0,0 +1,2 @@ +export type Dictionary = { +}; diff --git a/source/app/src/pages/WelcomePage.tsx b/source/app/src/pages/WelcomePage.tsx new file mode 100644 index 0000000..d88d657 --- /dev/null +++ b/source/app/src/pages/WelcomePage.tsx @@ -0,0 +1,18 @@ +// @ts-nocheck +import React from 'react'; + +import Section from '@prokyon/components/Section'; + +export const WelcomePage = () => { + return ( +
+ Seedling app generator. +
+
+ Tento web používá pouze technické cookie. Monitorování návštěvnosti je zcela anonymní a je prováděno na straně + provozovatele webu. +
+
+
+ ); +}; diff --git a/source/app/src/styles/components/component.js b/source/app/src/styles/components/component.js new file mode 100644 index 0000000..353cfc5 --- /dev/null +++ b/source/app/src/styles/components/component.js @@ -0,0 +1,8 @@ +const component = () => ({ +}); + +module.exports = ({ addComponents, theme }) => { + addComponents(component(theme)); +}; + +module.exports.component = component; diff --git a/source/app/src/styles/global.css b/source/app/src/styles/global.css new file mode 100644 index 0000000..6ba81f9 --- /dev/null +++ b/source/app/src/styles/global.css @@ -0,0 +1,11 @@ +@import '@prokyon/styles/global.css'; + +@import "./variables.css"; + +@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;500;700&display=swap'); + +body { + @apply bg-white text-black text-sm; + font-family: 'Poppins', sans-serif; + font-weight: lighter; +} \ No newline at end of file diff --git a/source/app/src/styles/postcss.config.js b/source/app/src/styles/postcss.config.js new file mode 100644 index 0000000..5b13d62 --- /dev/null +++ b/source/app/src/styles/postcss.config.js @@ -0,0 +1,4 @@ +const postcss = require('@prokyon/styles/postcss.config'); +const configPath = require.resolve('./tailwind.config.js'); + +module.exports = postcss({ tailwindConfigFile: configPath }); diff --git a/source/app/src/styles/tailwind.config.js b/source/app/src/styles/tailwind.config.js new file mode 100644 index 0000000..b0a31d0 --- /dev/null +++ b/source/app/src/styles/tailwind.config.js @@ -0,0 +1,23 @@ +const path = require('path'); + +const plugin = require('tailwindcss/plugin'); + +module.exports = { + presets: [require('@prokyon/styles/tailwind.config')], + content: [ + path.resolve(__dirname + '../../**/*.{js,ts,tsx}'), + path.resolve(__dirname + '../../../node_modules/@prokyon/**/*.{js,ts,tsx}'), + ], + safelist: require('@prokyon/styles/tailwind.config').safelist, + theme: { + extend: require('./theme'), + }, + variants: { + extend: { + borderWidth: ['last'], + }, + }, + plugins: [ + plugin(require('./components/component')), + ], +}; diff --git a/source/app/src/styles/theme.js b/source/app/src/styles/theme.js new file mode 100644 index 0000000..669cf14 --- /dev/null +++ b/source/app/src/styles/theme.js @@ -0,0 +1,10 @@ +const { pallete } = require('@prokyon/styles/utils/color'); + +module.exports = { + colors: { + disabled: pallete('disabled'), + }, + borderWidth: { + 1: '1px', + }, +}; diff --git a/source/app/src/styles/variables.css b/source/app/src/styles/variables.css new file mode 100644 index 0000000..241565b --- /dev/null +++ b/source/app/src/styles/variables.css @@ -0,0 +1,5 @@ +:root { + --color-h: 0; + --color-s: 0%; + --color-l: 0%; +} \ No newline at end of file diff --git a/source/app/src/types/global.ts b/source/app/src/types/global.ts new file mode 100644 index 0000000..e69de29 diff --git a/source/app/src/types/yup.d.ts b/source/app/src/types/yup.d.ts new file mode 100644 index 0000000..75cf9d1 --- /dev/null +++ b/source/app/src/types/yup.d.ts @@ -0,0 +1,8 @@ +// @ts-nocheck +import { StringSchema } from 'yup'; + +declare module 'yup' { + interface StringSchema { + // place custom Yup definition of validation + } +} diff --git a/source/app/src/utils/yup.ts b/source/app/src/utils/yup.ts new file mode 100644 index 0000000..c32eb77 --- /dev/null +++ b/source/app/src/utils/yup.ts @@ -0,0 +1,10 @@ +// @ts-nocheck +// place import for custom Yup validations + +import * as Yup from 'yup'; + +Yup.setLocale({ + mixed: { + required: 'Prosím, vyplňtě toto pole.', + }, +}); diff --git a/source/app/tests/nightwatchjs.d.ts b/source/app/tests/nightwatchjs.d.ts new file mode 100644 index 0000000..8444a92 --- /dev/null +++ b/source/app/tests/nightwatchjs.d.ts @@ -0,0 +1,9 @@ +import { Definition } from 'nightwatch'; + +declare module 'nightwatch' { + interface NightwatchCustomPageObjects { + } + + export interface NightwatchCustomCommands { + } +} diff --git a/source/app/tests/tsconfig.json b/source/app/tests/tsconfig.json new file mode 100644 index 0000000..c834f18 --- /dev/null +++ b/source/app/tests/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "outDir": ".nightwatchjs", + "baseUrl": ".", + "rootDir": ".", + "allowJs": true, + "skipLibCheck": true, + "module": "commonjs", + "strictNullChecks": true, + "strictFunctionTypes": false, + "target": "es6", + "lib": [ + "es6", + ], + }, + "files": [ + "./nightwatchjs.d.ts" + ], + "include": [ + "./**/*.ts", + ] +} \ No newline at end of file diff --git a/source/app/tsconfig.json b/source/app/tsconfig.json new file mode 100644 index 0000000..fc32b8f --- /dev/null +++ b/source/app/tsconfig.json @@ -0,0 +1,53 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "jsx": "react-jsx", + "baseUrl": ".", + "paths": { + "api/*": [ + "./src/api/*" + ], + "app/*": [ + "./src/app/*" + ], + "components/*": [ + "./src/components/*" + ], + "constants/*": [ + "./src/constants/*" + ], + "features/*": [ + "./src/features/*" + ], + "hooks/*": [ + "./src/hooks/*" + ], + "localization/*": [ + "./src/localization/*" + ], + "pages/*": [ + "./src/pages/*" + ], + "utils/*": [ + "./src/utils/*" + ], + "types/*": [ + "./src/types/*" + ] + }, + "types": [ + "./src/types/yup", + "node" + ], + }, + "include": [ + "./src/**/*.ts", + "./src/**/*.tsx", + "./types/modules.d.ts", + "src/styles/theme.js" + ], + "exclude": [ + "./node_modules", + "./src/**/__tests__/*.tsx" + ] +} \ No newline at end of file diff --git a/source/shared/.editorconfig b/source/shared/.editorconfig new file mode 100644 index 0000000..2593d1a --- /dev/null +++ b/source/shared/.editorconfig @@ -0,0 +1,12 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = tab +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = false \ No newline at end of file diff --git a/source/shared/.gitignore b/source/shared/.gitignore new file mode 100644 index 0000000..d163863 --- /dev/null +++ b/source/shared/.gitignore @@ -0,0 +1 @@ +build/ \ No newline at end of file diff --git a/source/shared/.npmrc b/source/shared/.npmrc new file mode 100644 index 0000000..e99c0c6 --- /dev/null +++ b/source/shared/.npmrc @@ -0,0 +1,2 @@ +always-auth=true +@prokyon:registry=https://npm.romanjaros.dev diff --git a/source/shared/.prettierrc b/source/shared/.prettierrc new file mode 100644 index 0000000..aaf36c9 --- /dev/null +++ b/source/shared/.prettierrc @@ -0,0 +1,9 @@ +{ + "useTabs": true, + "tabWidth": 2, + "semi": true, + "singleQuote": true, + "parser": "typescript", + "printWidth": 120, + "bracketSameLine": true +} \ No newline at end of file diff --git a/source/shared/Jenkinsfile b/source/shared/Jenkinsfile new file mode 100644 index 0000000..676e963 --- /dev/null +++ b/source/shared/Jenkinsfile @@ -0,0 +1,8 @@ +@Library('jenkins-lib') +import FrontendBuild + +FrontendBuild({ + name = '$(appName)' + port = '$(appPort):80' + runSonar = true +}) diff --git a/source/shared/package.json b/source/shared/package.json new file mode 100644 index 0000000..9d80cee --- /dev/null +++ b/source/shared/package.json @@ -0,0 +1,14 @@ +{ + "name": "$(appName)", + "version": "0.1.0", + "author": "Roman Jaroš", + "license": "ISC", + "scripts": { + "dev": "pnpm -r dev" + }, + "dependencies": {}, + "devDependencies": { + "@types/node": "18.11.9" + }, + "peerDependencies": {} +} \ No newline at end of file diff --git a/source/shared/pnpm-workspace.yaml b/source/shared/pnpm-workspace.yaml new file mode 100644 index 0000000..d698597 --- /dev/null +++ b/source/shared/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +packages: + - "apps/*" \ No newline at end of file diff --git a/source/shared/sonar-project.properties b/source/shared/sonar-project.properties new file mode 100644 index 0000000..c199f74 --- /dev/null +++ b/source/shared/sonar-project.properties @@ -0,0 +1,5 @@ +sonar.projectKey=$(appName) +sonar.projectName=$(appName) +sonar.inclusions=apps/** +sonar.coverage.exclusions=**/__tests__/** +sonar.javascript.lcov.reportPaths=apps/**/jest/lcov.info \ No newline at end of file diff --git a/source/shared/tsconfig.json b/source/shared/tsconfig.json new file mode 100644 index 0000000..cb56d47 --- /dev/null +++ b/source/shared/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "noImplicitAny": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "skipLibCheck": true, + "sourceMap": true, + "esModuleInterop": true, + "allowJs": true, + "outDir": "build", + "module": "commonjs", + "target": "es6", + "lib": [ + "es6", + "dom" + ], + "jsx": "react", + "types": [ + "jest", + ], + }, + "exclude": [ + "./node_modules", + "./src/**/__tests__/*.tsx" + ] +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..e69de29