diff --git a/docker-compose.yml b/docker-compose.yml index 87eef93..4855114 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -59,13 +59,13 @@ services: frontend: build: context: ./job-tracker-ui - # fork-ts-checker (CRA's build type-checker) needs more than Docker's default - # 64MB /dev/shm; too little causes a SIGSEGV during `npm run build`. + # Next's build type-checker needs more than Docker's default 64MB /dev/shm; too little + # causes a SIGSEGV during `npm run build`. shm_size: '1gb' args: - - REACT_APP_GOOGLE_CLIENT_ID=${AUTH_GOOGLE_CLIENT_ID} + - NEXT_PUBLIC_GOOGLE_CLIENT_ID=${AUTH_GOOGLE_CLIENT_ID} # Optional override; default in production is `/api` - - REACT_APP_API_BASE_URL=${REACT_APP_API_BASE_URL} + - NEXT_PUBLIC_API_BASE_URL=${REACT_APP_API_BASE_URL} ports: - "3000:80" depends_on: diff --git a/job-tracker-ui/.gitignore b/job-tracker-ui/.gitignore index 4d29575..f26ed9f 100644 --- a/job-tracker-ui/.gitignore +++ b/job-tracker-ui/.gitignore @@ -10,6 +10,9 @@ # production /build +/out +/.next +next-env.d.ts # misc .DS_Store diff --git a/job-tracker-ui/Dockerfile b/job-tracker-ui/Dockerfile index c81266d..f761b7f 100644 --- a/job-tracker-ui/Dockerfile +++ b/job-tracker-ui/Dockerfile @@ -2,11 +2,11 @@ FROM node:20-alpine AS build WORKDIR /app -ARG REACT_APP_GOOGLE_CLIENT_ID -ARG REACT_APP_API_BASE_URL +ARG NEXT_PUBLIC_GOOGLE_CLIENT_ID +ARG NEXT_PUBLIC_API_BASE_URL -ENV REACT_APP_GOOGLE_CLIENT_ID=$REACT_APP_GOOGLE_CLIENT_ID -ENV REACT_APP_API_BASE_URL=$REACT_APP_API_BASE_URL +ENV NEXT_PUBLIC_GOOGLE_CLIENT_ID=$NEXT_PUBLIC_GOOGLE_CLIENT_ID +ENV NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL COPY package*.json ./ RUN npm ci @@ -17,7 +17,7 @@ RUN npm run build FROM nginx:1.29.8-alpine COPY nginx.conf /etc/nginx/conf.d/default.conf -COPY --from=build /app/build /usr/share/nginx/html +COPY --from=build /app/out /usr/share/nginx/html EXPOSE 80 diff --git a/job-tracker-ui/app/layout.tsx b/job-tracker-ui/app/layout.tsx new file mode 100644 index 0000000..5f6b4be --- /dev/null +++ b/job-tracker-ui/app/layout.tsx @@ -0,0 +1,38 @@ +import type { Metadata, Viewport } from "next"; + +import "../src/index.css"; + +export const metadata: Metadata = { + title: "Jobbjakt", + description: "Jobbjakt — track and manage job applications", + manifest: "/manifest.json", + icons: { + icon: [ + { url: "/favicon.svg", type: "image/svg+xml" }, + { url: "/favicon.ico" }, + ], + apple: "/logo192.png", + }, +}; + +export const viewport: Viewport = { + width: "device-width", + initialScale: 1, + themeColor: "#15803d", +}; + +export default function RootLayout({ children }: { children: React.ReactNode }) { + return ( + + + + + + + + +
{children}
+ + + ); +} diff --git a/job-tracker-ui/app/page.tsx b/job-tracker-ui/app/page.tsx new file mode 100644 index 0000000..b08cf30 --- /dev/null +++ b/job-tracker-ui/app/page.tsx @@ -0,0 +1,12 @@ +"use client"; + +import dynamic from "next/dynamic"; + +// The whole app is a client-side React Router SPA whose providers read window/localStorage +// during their initial render -- ssr:false keeps Next's static prerender from ever executing +// any of it on the server. +const ClientApp = dynamic(() => import("../src/ClientApp"), { ssr: false }); + +export default function Page() { + return ; +} diff --git a/job-tracker-ui/next.config.js b/job-tracker-ui/next.config.js new file mode 100644 index 0000000..98299c8 --- /dev/null +++ b/job-tracker-ui/next.config.js @@ -0,0 +1,9 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + // The whole app is client-rendered React Router behind auth (see app/page.tsx) -- static + // export keeps the same "one index.html + JS bundle, served by nginx" deploy as CRA had. + output: "export", + reactStrictMode: true, +}; + +module.exports = nextConfig; diff --git a/job-tracker-ui/package-lock.json b/job-tracker-ui/package-lock.json index 40c78e9..c02d745 100644 --- a/job-tracker-ui/package-lock.json +++ b/job-tracker-ui/package-lock.json @@ -26,11 +26,12 @@ "@types/react-dom": "^19.2.3", "axios": "^1.15.0", "date-fns": "^4.1.0", + "next": "^16.2.10", "react": "^19.2.4", "react-dom": "^19.2.4", "react-router-dom": "^6.30.3", "react-scripts": "5.0.1", - "typescript": "^4.9.5", + "typescript": "^5.9.3", "web-vitals": "^2.1.4" } }, @@ -2401,6 +2402,16 @@ "postcss-selector-parser": "^6.0.10" } }, + "node_modules/@emnapi/runtime": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", + "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@emotion/babel-plugin": { "version": "11.13.5", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", @@ -2659,6 +2670,472 @@ "deprecated": "Use @eslint/object-schema instead", "license": "BSD-3-Clause" }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -3467,6 +3944,140 @@ "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/@next/env": { + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.10.tgz", + "integrity": "sha512-zLPxg9M0MEHmygpj5OuxjQ+vHMiy/K7cSp74G8ecYolmgUWw0RwN02tF56npup/+qaI8JB97hQgS/r2Hb6QwVA==", + "license": "MIT" + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.10.tgz", + "integrity": "sha512-v9IdJCa0H0mbo+8z5zwUpOk1Vj7RjkcI5uNYf5Ws1y6szf/p3Mzl9hLaST8SCt6L9h8NGnruZcd2+o0NTNwDhA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.10.tgz", + "integrity": "sha512-17IS0jJRViROGmA9uGdNR8VPJpfbnaVG7E9qhso5jDLkmyd0lSDORWxbcKINzcFqzZqGwGtMSnrFRxBpuUYjLQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.10.tgz", + "integrity": "sha512-GRQRsRtuciNJvB54AvvuQTiq0oZtFwa1owQqtZD8wwnGpM2L39MV22kpI72YSXLKIyY40LC66EiLFv4PiicXxg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.10.tgz", + "integrity": "sha512-zkN9MQYS7UQBro+FnISUq1itaQjXI9xqISzuQ+2bc921NcJ1x4yPCqrn77tVN6/dOOXaaWVX3k6/bR07pPwK+A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.10.tgz", + "integrity": "sha512-iCVJnwvrPYECvA6WM/7+oo+OiTvedIKLxtCLAZP4xZR3nXa1zmzZyLPbYCmWvpd4CvMYF1EMTafd0ii3DygLvA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.10.tgz", + "integrity": "sha512-ov2g4H0dHY9bPoOU83m91hWT7Iq5qy13bUnyyshLU3HGR1Ownn0X9QpmDPc5iIUaahTp7f7LeGAhV4DSFtackw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.10.tgz", + "integrity": "sha512-DwAnhLX76HQiFFQNgWlcK+JzlnD1rZ+UK/WY0ZMI/deXpvgnesjNYrqcfo1JzBuz4Kf7o3brIBL0glI1junatA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.10.tgz", + "integrity": "sha512-0JXq3b85Jk9Jg4ntLUbXSPvoDw3gpZou7twuKdoFG2jOw635v7+IiXfTaa0TxVMyx78pUjnrVYwLgjKfX4e6/A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@nicolo-ribaudo/eslint-scope-5-internals": { "version": "5.1.1-v1", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/eslint-scope-5-internals/-/eslint-scope-5-internals-5.1.1-v1.tgz", @@ -3957,6 +4568,15 @@ "url": "https://github.com/sponsors/gregberge" } }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, "node_modules/@tanstack/react-table": { "version": "8.21.3", "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.3.tgz", @@ -6242,6 +6862,12 @@ "node": ">=0.10.0" } }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -7215,6 +7841,16 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -12198,6 +12834,87 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "license": "MIT" }, + "node_modules/next": { + "version": "16.2.10", + "resolved": "https://registry.npmjs.org/next/-/next-16.2.10.tgz", + "integrity": "sha512-2som5AVXb3kE6Yjine3/mNbBayYF58eguBWIVVUdr1y/L426xyVEgYxgBG+1QC34P2x5E+tcDup6XkuOAX3dCA==", + "license": "MIT", + "dependencies": { + "@next/env": "16.2.10", + "@swc/helpers": "0.5.15", + "baseline-browser-mapping": "^2.9.19", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=20.9.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "16.2.10", + "@next/swc-darwin-x64": "16.2.10", + "@next/swc-linux-arm64-gnu": "16.2.10", + "@next/swc-linux-arm64-musl": "16.2.10", + "@next/swc-linux-x64-gnu": "16.2.10", + "@next/swc-linux-x64-musl": "16.2.10", + "@next/swc-win32-arm64-msvc": "16.2.10", + "@next/swc-win32-x64-msvc": "16.2.10", + "sharp": "^0.34.5" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.51.1", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/no-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", @@ -15519,6 +16236,51 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -16076,6 +16838,29 @@ "webpack": "^5.0.0" } }, + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, "node_modules/stylehacks": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", @@ -16932,16 +17717,16 @@ } }, "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/unbox-primitive": { diff --git a/job-tracker-ui/package.json b/job-tracker-ui/package.json index 6b9b143..20081d4 100644 --- a/job-tracker-ui/package.json +++ b/job-tracker-ui/package.json @@ -21,18 +21,19 @@ "@types/react-dom": "^19.2.3", "axios": "^1.15.0", "date-fns": "^4.1.0", + "next": "^16.2.10", "react": "^19.2.4", "react-dom": "^19.2.4", "react-router-dom": "^6.30.3", "react-scripts": "5.0.1", - "typescript": "^4.9.5", + "typescript": "^5.9.3", "web-vitals": "^2.1.4" }, "scripts": { - "start": "react-scripts start", - "build": "node --max-old-space-size=4096 ./node_modules/react-scripts/bin/react-scripts.js build", - "test": "react-scripts test", - "eject": "react-scripts eject" + "dev": "next dev", + "start": "next dev", + "build": "node --max-old-space-size=4096 ./node_modules/next/dist/bin/next build", + "test": "react-scripts test" }, "eslintConfig": { "extends": [ diff --git a/job-tracker-ui/public/index.html b/job-tracker-ui/public/index.html deleted file mode 100644 index 85eb36b..0000000 --- a/job-tracker-ui/public/index.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - Jobbjakt - - - -
- - diff --git a/job-tracker-ui/src/App.tsx b/job-tracker-ui/src/App.tsx index 4afeee7..8454325 100644 --- a/job-tracker-ui/src/App.tsx +++ b/job-tracker-ui/src/App.tsx @@ -27,11 +27,11 @@ import { PromptProvider } from "./prompt"; import JobTable from "./components/JobTable"; import type { JobTableColumns } from "./components/JobTable"; import { I18nProvider, useI18n } from "./i18n/I18nProvider"; -import LoginPage from "./pages/LoginPage"; -import LandingPage from "./pages/LandingPage"; -import ForgotPasswordPage from "./pages/ForgotPasswordPage"; -import ResetPasswordPage from "./pages/ResetPasswordPage"; -import RouteErrorPage from "./pages/RouteErrorPage"; +import LoginPage from "./views/LoginPage"; +import LandingPage from "./views/LandingPage"; +import ForgotPasswordPage from "./views/ForgotPasswordPage"; +import ResetPasswordPage from "./views/ResetPasswordPage"; +import RouteErrorPage from "./views/RouteErrorPage"; import { api } from "./api"; import { resolveCaptureUrl } from "./captureUrl"; import { clearAuthClientState, setAuthUserKey } from "./auth"; @@ -45,13 +45,13 @@ const CompaniesTable = lazy(() => import("./components/CompaniesTable")); const SettingsView = lazy(() => import("./components/SettingsView")); const RemindersView = lazy(() => import("./components/RemindersView")); const QuickCommandDialog = lazy(() => import("./components/QuickCommandDialog")); -const ProfilePage = lazy(() => import("./pages/ProfilePage")); -const AdminAuditPage = lazy(() => import("./pages/AdminAuditPage")); -const AdminUsersPage = lazy(() => import("./pages/AdminUsersPage")); -const AdminSystemPage = lazy(() => import("./pages/AdminSystemPage")); -const CorrespondenceInboxPage = lazy(() => import("./pages/CorrespondenceInboxPage")); -const GmailReviewPage = lazy(() => import("./pages/GmailReviewPage")); -const NotFoundPage = lazy(() => import("./pages/NotFoundPage")); +const ProfilePage = lazy(() => import("./views/ProfilePage")); +const AdminAuditPage = lazy(() => import("./views/AdminAuditPage")); +const AdminUsersPage = lazy(() => import("./views/AdminUsersPage")); +const AdminSystemPage = lazy(() => import("./views/AdminSystemPage")); +const CorrespondenceInboxPage = lazy(() => import("./views/CorrespondenceInboxPage")); +const GmailReviewPage = lazy(() => import("./views/GmailReviewPage")); +const NotFoundPage = lazy(() => import("./views/NotFoundPage")); type AuthConfig = { requireAuth: boolean }; type MeResponse = { diff --git a/job-tracker-ui/src/ClientApp.tsx b/job-tracker-ui/src/ClientApp.tsx new file mode 100644 index 0000000..0731422 --- /dev/null +++ b/job-tracker-ui/src/ClientApp.tsx @@ -0,0 +1,20 @@ +"use client"; + +import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider"; +import { AdapterDateFns } from "@mui/x-date-pickers/AdapterDateFns"; + +import App from "./App"; +import ErrorBoundary from "./components/ErrorBoundary"; +import { I18nProvider } from "./i18n/I18nProvider"; + +export default function ClientApp() { + return ( + + + + + + + + ); +} diff --git a/job-tracker-ui/src/LandingPage.authRedirect.test.tsx b/job-tracker-ui/src/LandingPage.authRedirect.test.tsx index 323ff77..ed707f2 100644 --- a/job-tracker-ui/src/LandingPage.authRedirect.test.tsx +++ b/job-tracker-ui/src/LandingPage.authRedirect.test.tsx @@ -4,7 +4,7 @@ import { render, screen, waitFor } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { MemoryRouter, Route, Routes, useLocation } from "react-router-dom"; import { api } from "./api"; -import LandingPage from "./pages/LandingPage"; +import LandingPage from "./views/LandingPage"; jest.mock("./api", () => ({ api: { diff --git a/job-tracker-ui/src/admin-system-page.test.tsx b/job-tracker-ui/src/admin-system-page.test.tsx index cb09087..9c25784 100644 --- a/job-tracker-ui/src/admin-system-page.test.tsx +++ b/job-tracker-ui/src/admin-system-page.test.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { fireEvent, render, screen, waitFor } from '@testing-library/react'; -import AdminSystemPage from './pages/AdminSystemPage'; +import AdminSystemPage from './views/AdminSystemPage'; import { I18nProvider } from './i18n/I18nProvider'; import { api } from './api'; diff --git a/job-tracker-ui/src/api.ts b/job-tracker-ui/src/api.ts index 682c842..ff477b5 100644 --- a/job-tracker-ui/src/api.ts +++ b/job-tracker-ui/src/api.ts @@ -35,7 +35,7 @@ export function getApiErrorMessage(error: any, fallback = "Request failed.") { return fallback; } -const envBaseUrl = process.env.REACT_APP_API_BASE_URL; +const envBaseUrl = process.env.NEXT_PUBLIC_API_BASE_URL; const defaultBaseUrl = window.location.hostname === "localhost" ? "http://localhost:5202/api" diff --git a/job-tracker-ui/src/assets/JobbjaktMark.tsx b/job-tracker-ui/src/assets/JobbjaktMark.tsx new file mode 100644 index 0000000..ec8f502 --- /dev/null +++ b/job-tracker-ui/src/assets/JobbjaktMark.tsx @@ -0,0 +1,21 @@ +import React from "react"; + +export default function JobbjaktMark(props: React.SVGProps) { + return ( + + + + + + + + + + + + + + + + ); +} diff --git a/job-tracker-ui/src/assets/jobbbjakt-mark.svg b/job-tracker-ui/src/assets/jobbbjakt-mark.svg deleted file mode 100644 index 863b558..0000000 --- a/job-tracker-ui/src/assets/jobbbjakt-mark.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/job-tracker-ui/src/components/GoogleAuthCard.tsx b/job-tracker-ui/src/components/GoogleAuthCard.tsx index fc5b62b..732a5c6 100644 --- a/job-tracker-ui/src/components/GoogleAuthCard.tsx +++ b/job-tracker-ui/src/components/GoogleAuthCard.tsx @@ -53,7 +53,7 @@ export default function GoogleAuthCard({ onSignedIn }: { onSignedIn?: () => void const [working, setWorking] = useState(false); const hostRef = useRef(null); - const clientId = (process.env.REACT_APP_GOOGLE_CLIENT_ID || "").trim(); + const clientId = (process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID || "").trim(); const signedIn = Boolean(me?.provider); const actionLabel = !signedIn ? t("continueWithGoogle") diff --git a/job-tracker-ui/src/correspondence-inbox-page.test.tsx b/job-tracker-ui/src/correspondence-inbox-page.test.tsx index c098e6b..944bcf1 100644 --- a/job-tracker-ui/src/correspondence-inbox-page.test.tsx +++ b/job-tracker-ui/src/correspondence-inbox-page.test.tsx @@ -4,7 +4,7 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; import { ToastProvider } from './toast'; import { I18nProvider } from './i18n/I18nProvider'; -import CorrespondenceInboxPage from './pages/CorrespondenceInboxPage'; +import CorrespondenceInboxPage from './views/CorrespondenceInboxPage'; import { api } from './api'; jest.mock('./api', () => ({ diff --git a/job-tracker-ui/src/gmail-review-page.test.tsx b/job-tracker-ui/src/gmail-review-page.test.tsx index f39c6ce..30db7d0 100644 --- a/job-tracker-ui/src/gmail-review-page.test.tsx +++ b/job-tracker-ui/src/gmail-review-page.test.tsx @@ -4,7 +4,7 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; import { ToastProvider } from './toast'; import { I18nProvider } from './i18n/I18nProvider'; -import GmailReviewPage from './pages/GmailReviewPage'; +import GmailReviewPage from './views/GmailReviewPage'; import { api } from './api'; jest.mock('./api', () => ({ diff --git a/job-tracker-ui/src/i18n/translations.ts b/job-tracker-ui/src/i18n/translations.ts index fae2022..27b79aa 100644 --- a/job-tracker-ui/src/i18n/translations.ts +++ b/job-tracker-ui/src/i18n/translations.ts @@ -608,7 +608,7 @@ export const translations = { adminSystemCpuMode: "CPU mode", adminSystemNoSmtpHost: "No SMTP host configured", googleAccountTitle: "Google account", - googleSetupHint: "Set `REACT_APP_GOOGLE_CLIENT_ID` in your UI environment to enable Google sign-in and account linking.", + googleSetupHint: "Set `NEXT_PUBLIC_GOOGLE_CLIENT_ID` in your UI environment to enable Google sign-in and account linking.", googleLinked: "Linked", googleAvailableToLink: "Available to link", googleLinkedDate: "Linked {date}", @@ -1552,7 +1552,7 @@ export const translations = { adminSystemCpuMode: "CPU-modus", adminSystemNoSmtpHost: "Ingen SMTP-vert konfigurert", googleAccountTitle: "Google-konto", - googleSetupHint: "Sett `REACT_APP_GOOGLE_CLIENT_ID` i UI-miljøet ditt for å aktivere Google-innlogging og kontokobling.", + googleSetupHint: "Sett `NEXT_PUBLIC_GOOGLE_CLIENT_ID` i UI-miljøet ditt for å aktivere Google-innlogging og kontokobling.", googleLinked: "Koblet", googleAvailableToLink: "Tilgjengelig for kobling", googleLinkedDate: "Koblet {date}", diff --git a/job-tracker-ui/src/index.tsx b/job-tracker-ui/src/index.tsx deleted file mode 100644 index 7079903..0000000 --- a/job-tracker-ui/src/index.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; -import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; -import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns'; -import './index.css'; -import App from './App'; -import reportWebVitals from './reportWebVitals'; -import ErrorBoundary from "./components/ErrorBoundary"; -import { I18nProvider } from './i18n/I18nProvider'; - -const root = ReactDOM.createRoot( - document.getElementById('root') as HTMLElement -); -root.render( - - - - - - - - - -); - -// If you want to start measuring performance in your app, pass a function -// to log results (for example: reportWebVitals(console.log)) -// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals -reportWebVitals(); diff --git a/job-tracker-ui/src/layout/AppShell.tsx b/job-tracker-ui/src/layout/AppShell.tsx index e7ffc2f..487c8ce 100644 --- a/job-tracker-ui/src/layout/AppShell.tsx +++ b/job-tracker-ui/src/layout/AppShell.tsx @@ -25,7 +25,7 @@ import MenuOpenIcon from "@mui/icons-material/MenuOpen"; import NotificationsNoneIcon from "@mui/icons-material/NotificationsNone"; import SettingsOutlinedIcon from "@mui/icons-material/SettingsOutlined"; -import { ReactComponent as JobbjaktMark } from "../assets/jobbbjakt-mark.svg"; +import JobbjaktMark from "../assets/JobbjaktMark"; import { useI18n } from "../i18n/I18nProvider"; export type NavItem = { diff --git a/job-tracker-ui/src/login-page.test.tsx b/job-tracker-ui/src/login-page.test.tsx index c26ae53..357f6a6 100644 --- a/job-tracker-ui/src/login-page.test.tsx +++ b/job-tracker-ui/src/login-page.test.tsx @@ -3,7 +3,7 @@ import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { MemoryRouter } from 'react-router-dom'; -import LoginPage from './pages/LoginPage'; +import LoginPage from './views/LoginPage'; import { ToastProvider } from './toast'; import { I18nProvider } from './i18n/I18nProvider'; import { api } from './api'; diff --git a/job-tracker-ui/src/profile-page.test.tsx b/job-tracker-ui/src/profile-page.test.tsx index b366cc1..b143415 100644 --- a/job-tracker-ui/src/profile-page.test.tsx +++ b/job-tracker-ui/src/profile-page.test.tsx @@ -3,7 +3,7 @@ import '@testing-library/jest-dom'; import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import { ToastProvider } from './toast'; import { I18nProvider } from './i18n/I18nProvider'; -import ProfilePage from './pages/ProfilePage'; +import ProfilePage from './views/ProfilePage'; import { api } from './api'; const createObjectURLMock = jest.fn(() => 'blob:mock-pdf'); diff --git a/job-tracker-ui/src/react-app-env.d.ts b/job-tracker-ui/src/react-app-env.d.ts deleted file mode 100644 index 6431bc5..0000000 --- a/job-tracker-ui/src/react-app-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/job-tracker-ui/src/reportWebVitals.ts b/job-tracker-ui/src/reportWebVitals.ts deleted file mode 100644 index 49a2a16..0000000 --- a/job-tracker-ui/src/reportWebVitals.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { ReportHandler } from 'web-vitals'; - -const reportWebVitals = (onPerfEntry?: ReportHandler) => { - if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { - getCLS(onPerfEntry); - getFID(onPerfEntry); - getFCP(onPerfEntry); - getLCP(onPerfEntry); - getTTFB(onPerfEntry); - }); - } -}; - -export default reportWebVitals; diff --git a/job-tracker-ui/src/pages/AdminAuditPage.tsx b/job-tracker-ui/src/views/AdminAuditPage.tsx similarity index 100% rename from job-tracker-ui/src/pages/AdminAuditPage.tsx rename to job-tracker-ui/src/views/AdminAuditPage.tsx diff --git a/job-tracker-ui/src/pages/AdminSystemPage.tsx b/job-tracker-ui/src/views/AdminSystemPage.tsx similarity index 100% rename from job-tracker-ui/src/pages/AdminSystemPage.tsx rename to job-tracker-ui/src/views/AdminSystemPage.tsx diff --git a/job-tracker-ui/src/pages/AdminUsersPage.tsx b/job-tracker-ui/src/views/AdminUsersPage.tsx similarity index 100% rename from job-tracker-ui/src/pages/AdminUsersPage.tsx rename to job-tracker-ui/src/views/AdminUsersPage.tsx diff --git a/job-tracker-ui/src/pages/CorrespondenceInboxPage.tsx b/job-tracker-ui/src/views/CorrespondenceInboxPage.tsx similarity index 100% rename from job-tracker-ui/src/pages/CorrespondenceInboxPage.tsx rename to job-tracker-ui/src/views/CorrespondenceInboxPage.tsx diff --git a/job-tracker-ui/src/pages/ForgotPasswordPage.tsx b/job-tracker-ui/src/views/ForgotPasswordPage.tsx similarity index 100% rename from job-tracker-ui/src/pages/ForgotPasswordPage.tsx rename to job-tracker-ui/src/views/ForgotPasswordPage.tsx diff --git a/job-tracker-ui/src/pages/GmailReviewPage.tsx b/job-tracker-ui/src/views/GmailReviewPage.tsx similarity index 100% rename from job-tracker-ui/src/pages/GmailReviewPage.tsx rename to job-tracker-ui/src/views/GmailReviewPage.tsx diff --git a/job-tracker-ui/src/pages/LandingPage.tsx b/job-tracker-ui/src/views/LandingPage.tsx similarity index 100% rename from job-tracker-ui/src/pages/LandingPage.tsx rename to job-tracker-ui/src/views/LandingPage.tsx diff --git a/job-tracker-ui/src/pages/LoginPage.tsx b/job-tracker-ui/src/views/LoginPage.tsx similarity index 100% rename from job-tracker-ui/src/pages/LoginPage.tsx rename to job-tracker-ui/src/views/LoginPage.tsx diff --git a/job-tracker-ui/src/pages/NotFoundPage.tsx b/job-tracker-ui/src/views/NotFoundPage.tsx similarity index 100% rename from job-tracker-ui/src/pages/NotFoundPage.tsx rename to job-tracker-ui/src/views/NotFoundPage.tsx diff --git a/job-tracker-ui/src/pages/ProfilePage.tsx b/job-tracker-ui/src/views/ProfilePage.tsx similarity index 100% rename from job-tracker-ui/src/pages/ProfilePage.tsx rename to job-tracker-ui/src/views/ProfilePage.tsx diff --git a/job-tracker-ui/src/pages/ResetPasswordPage.tsx b/job-tracker-ui/src/views/ResetPasswordPage.tsx similarity index 100% rename from job-tracker-ui/src/pages/ResetPasswordPage.tsx rename to job-tracker-ui/src/views/ResetPasswordPage.tsx diff --git a/job-tracker-ui/src/pages/RouteErrorPage.tsx b/job-tracker-ui/src/views/RouteErrorPage.tsx similarity index 100% rename from job-tracker-ui/src/pages/RouteErrorPage.tsx rename to job-tracker-ui/src/views/RouteErrorPage.tsx diff --git a/job-tracker-ui/tsconfig.json b/job-tracker-ui/tsconfig.json index a273b0c..27417e9 100644 --- a/job-tracker-ui/tsconfig.json +++ b/job-tracker-ui/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es5", + "target": "es2017", "lib": [ "dom", "dom.iterable", @@ -14,13 +14,26 @@ "forceConsistentCasingInFileNames": true, "noFallthroughCasesInSwitch": true, "module": "esnext", - "moduleResolution": "node", + "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, + "incremental": true, "noEmit": true, - "jsx": "react-jsx" + "jsx": "react-jsx", + "plugins": [ + { + "name": "next" + } + ] }, "include": [ - "src" + "src", + "app", + "next-env.d.ts", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts" + ], + "exclude": [ + "node_modules" ] }