diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..a72c1e59 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +node_modules +tmp +.vscode +.idea \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..33602537 --- /dev/null +++ b/.env.example @@ -0,0 +1,8 @@ +# FORMS +DATAJEGER_EMAIL_ADDRESS=datajegeren.staging@norge.no +FDK_MAIL_SERVICE_ENDPOINT=mail-sender-service.staging.fellesdatakatalog.digdir.no/api/sendmail +FDK_MAIL_SERVICE_API_KEY= +FDK_BASE_URI=https://staging.fellesdatakatalog.digdir.no +FDK_COMMUNITY_BASE_URI=https://community.staging.fellesdatakatalog.digdir.no +FDK_REGISTRATION_BASE_URI=https://registrering.staging.fellesdatakatalog.digdir.no +# diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 00000000..04c6b8c6 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,121 @@ +{ + "root": true, + "ignorePatterns": ["node_modules", "**/*"], + "plugins": ["@nx"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": { + "@nx/enforce-module-boundaries": [ + "error", + { + "enforceBuildableLibDependency": true, + "allow": [], + "depConstraints": [ + { + "sourceTag": "*", + "onlyDependOnLibsWithTags": ["*"] + } + ] + } + ] + } + }, + { + "files": ["*.ts", "*.tsx"], + "extends": [ + "plugin:import/recommended", + "plugin:@nx/typescript", + "plugin:@typescript-eslint/eslint-plugin/recommended", + "eslint:recommended", + "plugin:react/recommended", + "plugin:jsx-a11y/recommended", + "prettier" + ], + "rules": { + "import/no-unresolved": "off", + "react/react-in-jsx-scope": "off", + "no-console": "off", + "no-restricted-syntax": [ + "error", + { + "selector": "CallExpression[callee.object.name='console'][callee.property.name!=/^(error)$/]", + "message": "Unexpected property on console object was called" + } + ], + "no-duplicate-imports": "error", + "arrow-body-style": ["warn", "as-needed"], + "require-atomic-updates": "error", + "no-use-before-define": "error", + "no-unreachable-loop": "error", + "no-unmodified-loop-condition": "error", + "no-promise-executor-return": "error", + "no-await-in-loop": "error", + "camelcase": "error", + "complexity": ["warn", 10], + "consistent-return": "warn", + "curly": "warn", + "default-case": "error", + "default-case-last": "error", + "default-param-last": "error", + "eqeqeq": ["error", "smart"], + "func-style": ["error", "expression", { "allowArrowFunctions": true }], + "max-depth": ["warn", 4], + "max-lines": ["warn", 300], + "no-alert": "warn", + "no-empty-function": "warn", + "no-eq-null": "error", + "no-implicit-coercion": "warn", + "no-magic-numbers": ["warn", { "ignoreArrayIndexes": true }], + "no-param-reassign": "error", + "no-shadow": "off", + "@typescript-eslint/no-shadow": "error", + "prefer-const": "error", + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": "error" + } + }, + { + "files": ["*.js", "*.jsx"], + "extends": [ + "plugin:import/recommended", + "plugin:@nx/javascript", + "eslint:recommended", + "plugin:react/recommended", + "plugin:jsx-a11y/recommended", + "prettier" + ], + "rules": { + "react/react-in-jsx-scope": "off", + "no-console": "warn", + "no-duplicate-imports": "error", + "arrow-body-style": ["warn", "as-needed"], + "require-atomic-updates": "error", + "no-use-before-define": "error", + "no-unreachable-loop": "error", + "no-unmodified-loop-condition": "error", + "no-promise-executor-return": "error", + "no-await-in-loop": "error", + "camelcase": "error", + "complexity": ["warn", 10], + "consistent-return": "warn", + "curly": "warn", + "default-case": "error", + "default-case-last": "error", + "default-param-last": "error", + "eqeqeq": ["error", "smart"], + "func-style": ["error", "expression", { "allowArrowFunctions": true }], + "max-depth": ["warn", 4], + "max-lines": ["warn", 300], + "no-alert": "warn", + "no-empty-function": "warn", + "no-eq-null": "error", + "no-implicit-coercion": "warn", + "no-magic-numbers": ["warn", { "ignoreArrayIndexes": true }], + "no-param-reassign": "error", + "no-shadow": "error", + "prefer-const": "error" + } + } + ] +} diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..53f69098 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,16 @@ +--- +name: Bug rapport +about: Lag en bug rapport +title: '[Bug]: kort beskrivelse av problemet' +labels: 'bug' +--- + +## 🐛 Bug rapport + +### Nåværende oppførsel + +### Forventet oppførsel + +### Hvordan reprodusere? + +### Forslag/Løsning [Valgfritt] diff --git a/.github/ISSUE_TEMPLATE/feature_requests.md b/.github/ISSUE_TEMPLATE/feature_requests.md new file mode 100644 index 00000000..306cd1b8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_requests.md @@ -0,0 +1,16 @@ +--- +name: Feature forespørsel +about: Forespør en ny feature +title: '[Feat]: kort beskrivelse av feature' +labels: 'enhancement' +--- + +## 🚀 Feature forespørsel + +### Feature beskrivelse + +### Hvorfor trenger vi det? + +### Forslag/løsning [Valgfritt] + +### Definisjon av ferdig diff --git a/.github/ISSUE_TEMPLATE/ux_issue.md b/.github/ISSUE_TEMPLATE/ux_issue.md new file mode 100644 index 00000000..689e497d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/ux_issue.md @@ -0,0 +1,16 @@ +--- +name: UX issue +about: Lag et UX issue +title: '[UX]: kort beskrivelse av problemet' +labels: 'ux' +--- + +## 🪄 UX rapport + +### Nåværende oppførsel + +### Dette er et problem fordi: + +### Forventet oppførsel/ønsket oppførsel + +### Skjermbilder diff --git a/.github/workflows/codeql.yaml b/.github/workflows/codeql.yaml new file mode 100644 index 00000000..9e81a31d --- /dev/null +++ b/.github/workflows/codeql.yaml @@ -0,0 +1,22 @@ +name: Code scanning (CodeQL) + +on: + pull_request: + types: [ready_for_review, opened, reopened, synchronize] + branches: + - main + push: + branches: + - main + schedule: + - cron: '0 2 * * *' + +jobs: + codeql: + name: Run codeql scan + if: github.event.pull_request.draft == false + uses: Informasjonsforvaltning/workflows/.github/workflows/codeql.yaml@main + with: + language: javascript + secrets: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/deploy-prod&demo.yaml b/.github/workflows/deploy-prod&demo.yaml new file mode 100644 index 00000000..213a87f8 --- /dev/null +++ b/.github/workflows/deploy-prod&demo.yaml @@ -0,0 +1,118 @@ +name: Deploy to production and demo + +on: + push: + branches: + - main + workflow_dispatch: + +jobs: + test: + name: Test and build + if: github.event.pull_request.draft == false + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + changed: ${{ steps.set-matrix.outputs.changed }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + # We need to fetch all branches and commits so that Nx affected has a base to compare against. + fetch-depth: 0 + + - name: Set SHAs + uses: nrwl/nx-set-shas@v4 + + - name: Enable corepack + run: corepack enable + + - name: Use Node.js 20.9.0 + uses: actions/setup-node@v4 + with: + node-version: 20.9.0 + cache: 'yarn' + + - name: Yarn install + run: | + corepack enable + yarn set version stable + yarn install --frozen-lockfile + + - name: Caching Nx + uses: actions/cache@v4 + with: + path: node_modules/.cache + key: cache-nx-${{ hashFiles('yarn.lock') }} + + - id: set-matrix + run: | + echo "matrix={\"appname\":$(yarn --silent run matrix)}" >> $GITHUB_OUTPUT + echo "changed=$(yarn --silent run matrix)" >> $GITHUB_OUTPUT + + - run: yarn run affected:lint --parallel=2 + - run: yarn run affected:test --parallel=2 --configuration=ci + - run: yarn run affected:e2e --parallel=2 --configuration=ci + - run: yarn run affected:build --parallel=2 --configuration=ci + + - name: Caching Dist Folder + uses: actions/cache@v4 + with: + path: ./dist + key: cache-dist-${{ github.sha }} + + build: + name: Build affected apps when pull request is created + needs: [test] + if: ${{ needs.test.outputs.changed != '[]' }} + strategy: + matrix: ${{fromJSON(needs.test.outputs.matrix)}} + uses: Informasjonsforvaltning/workflows/.github/workflows/build-push.yaml@main + with: + app_name: ${{ matrix.appname }}-frontend + environment: prod + build_env: true + build_env_name: BINARY + build_env_value: ${{ matrix.appname }}-frontend + cache_path: ./dist + cache_key: cache-dist-${{ github.sha }} + dockerfile: apps/${{ matrix.appname }}/Dockerfile + secrets: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GCP_SA_DIGDIR_FDK_GCR_KEY: ${{ secrets.GCP_SA_DIGDIR_FDK_GCR_KEY }} + + deploy-prod: + name: Deploy affected apps to production environment with reusable workflow + needs: [test, build] + strategy: + matrix: ${{fromJSON(needs.test.outputs.matrix)}} + fail-fast: false + if: ${{ needs.test.outputs.changed != '[]' }} + uses: Informasjonsforvaltning/workflows/.github/workflows/kustomize-deploy.yaml@main + with: + app_name: ${{ matrix.appname }}-frontend + environment: prod + monorepo_app: true + cluster: digdir-fdk-prod + secrets: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DIGDIR_FDK_AUTODEPLOY: ${{ secrets.DIGDIR_FDK_PROD_AUTODEPLOY }} + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + + deploy-demo: + name: Deploy affected apps to demo environment with reusable workflow + needs: [test, deploy-prod] + strategy: + matrix: ${{fromJSON(needs.test.outputs.matrix)}} + fail-fast: false + if: ${{ needs.test.outputs.changed != '[]' }} + uses: Informasjonsforvaltning/workflows/.github/workflows/kustomize-deploy.yaml@main + with: + app_name: ${{ matrix.appname }}-frontend + environment: demo + monorepo_app: true + cluster: digdir-fdk-dev + secrets: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DIGDIR_FDK_AUTODEPLOY: ${{ secrets.DIGDIR_FDK_DEV_AUTODEPLOY }} + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} diff --git a/.github/workflows/deploy-staging.yaml b/.github/workflows/deploy-staging.yaml new file mode 100644 index 00000000..8bd298b2 --- /dev/null +++ b/.github/workflows/deploy-staging.yaml @@ -0,0 +1,99 @@ +name: Deploy to staging + +on: + pull_request: + types: [ready_for_review, opened, reopened, synchronize] + branches: + - main + +jobs: + test: + name: Test and build + if: github.event.pull_request.draft == false + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + changed: ${{ steps.set-matrix.outputs.changed }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + # We need to fetch all branches and commits so that Nx affected has a base to compare against. + fetch-depth: 0 + + - name: Set SHAs + uses: nrwl/nx-set-shas@v4 + + - name: Enable corepack + run: corepack enable + + - name: Use Node.js 20.9.0 + uses: actions/setup-node@v4 + with: + node-version: 20.9.0 + cache: 'yarn' + + - name: Yarn install + run: | + yarn set version stable + yarn install --frozen-lockfile + + - name: Caching Nx + uses: actions/cache@v4 + with: + path: node_modules/.cache + key: cache-nx-${{ hashFiles('yarn.lock') }} + + - id: set-matrix + run: | + echo "matrix={\"appname\":$(yarn --silent run matrix)}" >> $GITHUB_OUTPUT + echo "changed=$(yarn --silent run matrix)" >> $GITHUB_OUTPUT + + - run: yarn run affected:lint --parallel=2 + - run: yarn run affected:test --parallel=2 --configuration=ci + - run: yarn run affected:e2e --parallel=2 --configuration=ci + - run: yarn run affected:build --parallel=2 --configuration=ci + + - name: Caching Dist Folder + uses: actions/cache@v4 + with: + path: ./dist + key: cache-dist-${{ github.sha }} + + build: + name: Build affected apps when pull request is created + needs: [test] + if: ${{ needs.test.outputs.changed != '[]' }} + strategy: + matrix: ${{fromJSON(needs.test.outputs.matrix)}} + uses: Informasjonsforvaltning/workflows/.github/workflows/build-push.yaml@main + with: + app_name: ${{ matrix.appname }}-frontend + environment: staging + build_env: true + build_env_name: BINARY + build_env_value: ${{ matrix.appname }}-frontend + cache_path: ./dist + cache_key: cache-dist-${{ github.sha }} + dockerfile: apps/${{ matrix.appname }}/Dockerfile + secrets: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GCP_SA_DIGDIR_FDK_GCR_KEY: ${{ secrets.GCP_SA_DIGDIR_FDK_GCR_KEY }} + + deploy: + name: Deploy affected apps to staging environment with reusable workflow + needs: [test, build] + if: ${{ needs.test.outputs.changed != '[]' }} + strategy: + matrix: ${{fromJSON(needs.test.outputs.matrix)}} + fail-fast: false + uses: Informasjonsforvaltning/workflows/.github/workflows/kustomize-deploy.yaml@main + with: + app_name: ${{ matrix.appname }}-frontend + environment: staging + monorepo_app: true + cluster: digdir-fdk-dev + secrets: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DIGDIR_FDK_AUTODEPLOY: ${{ secrets.DIGDIR_FDK_DEV_AUTODEPLOY }} + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..9f77fde5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,52 @@ +# See http://help.github.com/ignore-files/ for more about ignoring files. + +# compiled output +dist +tmp +/out-tsc + +# dependencies +node_modules + +# IDEs and editors +#.vscode is commented beacuse extension suggestions are useful +.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# misc +/.sass-cache +/connect.lock +/coverage +/libpeerconnection.log +npm-debug.log +yarn-error.log +testem.log +/typings + +# System Files +.DS_Store +Thumbs.db + +# NX +.nx + +# Next.js +.vercel +.next +out + +# local env files +.env* +/**/*/.env* +!**/.env*.example + +# yarn +.yarn + +# lint +.eslintcache diff --git a/.husky/.gitignore b/.husky/.gitignore new file mode 100644 index 00000000..31354ec1 --- /dev/null +++ b/.husky/.gitignore @@ -0,0 +1 @@ +_ diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 00000000..f2229532 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,7 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +npx lint-staged +yarn run affected:lint +yarn run affected:test +yarn run affected:e2e diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..da17b5ea --- /dev/null +++ b/.prettierignore @@ -0,0 +1,5 @@ +# Add files here to ignore them from prettier formatting +/dist +/coverage +/.nx +/node_modules diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..8d645ca9 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,8 @@ +{ + "printWidth": 120, + "useTabs": false, + "semi": true, + "singleQuote": true, + "jsxSingleQuote": true, + "singleAttributePerLine": true +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..7596d5c2 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + "recommendations": [ + "dbaeumer.vscode-eslint", + "ms-playwright.playwright", + "github.copilot", + "nrwl.angular-console", + "esbenp.prettier-vscode" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..2ee98c3d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnType": false, + "editor.formatOnPaste": true, + "editor.formatOnSave": true, + "editor.formatOnSaveMode": "file", + "editor.inlineSuggest.suppressSuggestions": false, + "files.insertFinalNewline": true, + "files.trimTrailingWhitespace": true, + "css.lint.validProperties": ["composes"] +} diff --git a/.yarnrc.yml b/.yarnrc.yml new file mode 100644 index 00000000..3186f3f0 --- /dev/null +++ b/.yarnrc.yml @@ -0,0 +1 @@ +nodeLinker: node-modules diff --git a/README.md b/README.md index f0b1b64c..663bb13d 100644 --- a/README.md +++ b/README.md @@ -1 +1,45 @@ -# fdk-frontend \ No newline at end of file +# fdk-frontend + +## Dependencies + +Make sure you have the following dependencies installed on your machine: + +- [Node.js](https://nodejs.org/en/download/) + - preferred installation method: [nvm](https://github.com/nvm-sh/nvm) +- [Yarn](https://yarnpkg.com/getting-started/install) +- Make sure you have an `.env` file at the root of the project like `.env.example` with the correct values + +## Start the application + +Run `yarn nx dev ` to start the development server. +Example: `yarn nx dev forms` + +## Build for production + +Run `yarn nx build forms` to build the application. The build artifacts are stored in the output directory (e.g. `dist/` or `build/`), ready to be deployed. + +## Examples + +#### Create a new component + +```bash +yarn nx g @nx/next:component my-component --project=ui --export +``` + +#### Create a new library + +```bash +yarn nx g @nx/next:lib my-lib --project=ui --export +``` + +For more information visit [Nx Next.js plugin](https://nx.dev/nx-api/next) + +## Integrate with editors + +Enhance your Nx experience by installing [Nx Console](https://nx.dev/nx-console) for your favorite editor. Nx Console +provides an interactive UI to view your projects, run tasks, generate code, and more! Available for VSCode, IntelliJ and +comes with a LSP for Vim users. + +## Editing text files + +You can find the text files used in the project in the `libs/dictionaries/src/lib/dictionaries` folder. diff --git a/apps/forms-e2e/.eslintrc.json b/apps/forms-e2e/.eslintrc.json new file mode 100644 index 00000000..cdbd057c --- /dev/null +++ b/apps/forms-e2e/.eslintrc.json @@ -0,0 +1,22 @@ +{ + "extends": ["plugin:playwright/recommended", "../../.eslintrc.json"], + "ignorePatterns": ["!**/*", "playwright.config.ts"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["src/**/*.{ts,js,tsx,jsx}"], + "rules": {} + } + ] +} diff --git a/apps/forms-e2e/playwright.config.ts b/apps/forms-e2e/playwright.config.ts new file mode 100644 index 00000000..325bd97e --- /dev/null +++ b/apps/forms-e2e/playwright.config.ts @@ -0,0 +1,69 @@ +import { defineConfig, devices } from '@playwright/test'; +import { nxE2EPreset } from '@nx/playwright/preset'; + +import { workspaceRoot } from '@nx/devkit'; + +// For CI, you may want to set BASE_URL to the deployed application. +const baseURL = process.env['BASE_URI'] || 'http://127.0.0.1:3000'; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// require('dotenv').config(); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + ...nxE2EPreset(__filename, { testDir: './src' }), + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + baseURL, + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + /* Run your local dev server before starting the tests */ + webServer: { + command: 'yarn nx dev forms', + url: 'http://127.0.0.1:3000', + reuseExistingServer: !process.env.CI, + cwd: workspaceRoot, + }, + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + + { + name: 'firefox', + use: { ...devices['Desktop Firefox'] }, + }, + + { + name: 'webkit', + use: { ...devices['Desktop Safari'] }, + }, + + // Uncomment for mobile browsers support + /* { + name: 'Mobile Chrome', + use: { ...devices['Pixel 5'] }, + }, + { + name: 'Mobile Safari', + use: { ...devices['iPhone 12'] }, + }, */ + + // Uncomment for branded browsers + /* { + name: 'Microsoft Edge', + use: { ...devices['Desktop Edge'], channel: 'msedge' }, + }, + { + name: 'Google Chrome', + use: { ...devices['Desktop Chrome'], channel: 'chrome' }, + } */ + ], +}); diff --git a/apps/forms-e2e/project.json b/apps/forms-e2e/project.json new file mode 100644 index 00000000..48fe7141 --- /dev/null +++ b/apps/forms-e2e/project.json @@ -0,0 +1,8 @@ +{ + "name": "forms-e2e", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/forms-e2e/src", + "implicitDependencies": ["forms"], + "// targets": "to see all targets run: nx show project forms-e2e --web", + "targets": {} +} diff --git a/apps/forms-e2e/src/example.spec.ts b/apps/forms-e2e/src/example.spec.ts new file mode 100644 index 00000000..d86a5303 --- /dev/null +++ b/apps/forms-e2e/src/example.spec.ts @@ -0,0 +1,8 @@ +import { test, expect } from '@playwright/test'; + +test('has title', async ({ page }) => { + await page.goto('/forms/data-hunter'); + + // Expect h1 to contain a substring. + expect(await page.title()).toBe('Forms'); +}); diff --git a/apps/forms-e2e/tsconfig.json b/apps/forms-e2e/tsconfig.json new file mode 100644 index 00000000..114364a1 --- /dev/null +++ b/apps/forms-e2e/tsconfig.json @@ -0,0 +1,19 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "allowJs": true, + "outDir": "../../dist/out-tsc", + "module": "commonjs", + "sourceMap": false + }, + "include": [ + "**/*.ts", + "**/*.js", + "playwright.config.ts", + "src/**/*.spec.ts", + "src/**/*.spec.js", + "src/**/*.test.ts", + "src/**/*.test.js", + "src/**/*.d.ts" + ] +} diff --git a/apps/forms/.eslintrc.json b/apps/forms/.eslintrc.json new file mode 100644 index 00000000..7d083203 --- /dev/null +++ b/apps/forms/.eslintrc.json @@ -0,0 +1,26 @@ +{ + "extends": ["plugin:@nx/react-typescript", "next", "next/core-web-vitals", "../../.eslintrc.json"], + "ignorePatterns": ["!**/*", ".next/**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": { + "@next/next/no-html-link-for-pages": ["error", "apps/forms/pages"] + } + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.spec.ts", "*.spec.tsx", "*.spec.js", "*.spec.jsx"], + "env": { + "jest": true + } + } + ] +} diff --git a/apps/forms/Dockerfile b/apps/forms/Dockerfile new file mode 100644 index 00000000..baaf94ad --- /dev/null +++ b/apps/forms/Dockerfile @@ -0,0 +1,8 @@ +FROM node:20-alpine3.18 +WORKDIR /app +COPY yarn.lock dist/apps/forms ./ +ENV NODE_ENV=$NODE_ENV +RUN corepack enable +RUN yarn set version stable +RUN yarn install +CMD yarn next start -p 8080 diff --git a/apps/forms/app/[lang]/data-hunter/components/data-hunter-form/data-hunter-form.module.css b/apps/forms/app/[lang]/data-hunter/components/data-hunter-form/data-hunter-form.module.css new file mode 100644 index 00000000..47566c2e --- /dev/null +++ b/apps/forms/app/[lang]/data-hunter/components/data-hunter-form/data-hunter-form.module.css @@ -0,0 +1,19 @@ +.textArea { + margin-top: 2rem; +} + +.textField { + width: 100%; + margin-top: 2rem; + align-self: flex-start; +} + +.textFieldHalfWith { + composes: textField; + width: 50%; +} + +.button { + margin-top: 3rem; + align-self: flex-start; +} diff --git a/apps/forms/app/[lang]/data-hunter/components/data-hunter-form/index.tsx b/apps/forms/app/[lang]/data-hunter/components/data-hunter-form/index.tsx new file mode 100644 index 00000000..f53e430b --- /dev/null +++ b/apps/forms/app/[lang]/data-hunter/components/data-hunter-form/index.tsx @@ -0,0 +1,104 @@ +'use client'; + +import { Paragraph, Textarea, Textfield } from '@digdir/designsystemet-react'; +import { LabelWithTag, SubmitButton } from '@fdk-frontend/ui'; +import { type Dictionary } from '@fdk-frontend/dictionaries'; +import { useFormState } from 'react-dom'; +import { sendEmailAction } from '../../../../actions'; +import { EMPTY_FORM_STATE, extractErrorMessages } from '@fdk-frontend/utils'; +import styles from './data-hunter-form.module.css'; + +type Props = { + dictionary: Dictionary; +}; + +const DataHunterForm = ({ dictionary }: Props) => { + const [state, formAction] = useFormState(sendEmailAction, EMPTY_FORM_STATE); + const textAreaCols = 100; + const textAreaRows = 5; + + return ( +
+