From f4390e7e860ccae943989d11caaf7a2fd049ada1 Mon Sep 17 00:00:00 2001 From: siddheshraze <81591724+siddheshraze@users.noreply.github.com> Date: Mon, 5 Aug 2024 15:10:22 -0400 Subject: [PATCH] 1. Applying prettier changes. 2. Amending Tooltip function in Dashboard's Chip to disappear when the pulse animation is active. 3. Amending datagrid tooltips to disable interactivity with the text itself, ensuring that user can move from one button to the next without accidentally holding the tooltip open. --- frontend/.eslintrc.js | 34 +- frontend/.prettierrc.json | 2 +- frontend/__tests__/api/cmid.test.tsx | 52 +- .../__tests__/api/cmprevalidation.test.tsx | 64 +- frontend/__tests__/api/fetchall.test.tsx | 62 +- .../api/filehandlers/deletefile.test.tsx | 48 +- .../filehandlers/downloadallfiles.test.tsx | 64 +- .../api/filehandlers/downloadfile.test.tsx | 68 +- .../api/filehandlers/storageload.test.tsx | 80 +- frontend/__tests__/loginpage.test.tsx | 32 +- frontend/__tests__/rollovermodal.test.tsx | 54 +- frontend/app/(hub)/dashboard/error.tsx | 8 +- frontend/app/(hub)/dashboard/page.tsx | 54 +- frontend/app/(hub)/error.tsx | 8 +- .../fixeddatainput/alltaxonomies/error.tsx | 8 +- .../fixeddatainput/alltaxonomies/page.tsx | 2 +- .../(hub)/fixeddatainput/attributes/error.tsx | 8 +- .../(hub)/fixeddatainput/attributes/page.tsx | 2 +- .../app/(hub)/fixeddatainput/census/error.tsx | 8 +- .../app/(hub)/fixeddatainput/census/page.tsx | 2 +- .../(hub)/fixeddatainput/personnel/error.tsx | 8 +- .../(hub)/fixeddatainput/personnel/page.tsx | 2 +- .../fixeddatainput/quadratpersonnel/error.tsx | 8 +- .../fixeddatainput/quadratpersonnel/page.tsx | 2 +- .../(hub)/fixeddatainput/quadrats/error.tsx | 8 +- .../(hub)/fixeddatainput/quadrats/page.tsx | 2 +- .../(hub)/fixeddatainput/species/error.tsx | 8 +- .../app/(hub)/fixeddatainput/species/page.tsx | 2 +- .../fixeddatainput/stemtaxonomies/error.tsx | 8 +- .../fixeddatainput/stemtaxonomies/page.tsx | 4 +- .../fixeddatainput/subquadrats/error.tsx | 8 +- .../(hub)/fixeddatainput/subquadrats/page.tsx | 2 +- frontend/app/(hub)/layout.tsx | 236 +++--- .../(hub)/measurementshub/summary/error.tsx | 8 +- .../(hub)/measurementshub/summary/page.tsx | 82 +- .../measurementshub/uploadedfiles/error.tsx | 8 +- .../measurementshub/uploadedfiles/page.tsx | 8 +- .../validationhistory/error.tsx | 8 +- .../validationhistory/page.tsx | 2 +- .../measurementshub/viewfulltable/error.tsx | 8 +- .../measurementshub/viewfulltable/page.tsx | 4 +- frontend/app/(login)/login/error.tsx | 8 +- frontend/app/(login)/login/page.tsx | 26 +- .../app/api/auth/[[...nextauth]]/route.ts | 26 +- .../[dataType]/[[...slugs]]/route.ts | 30 +- frontend/app/api/details/cmid/route.ts | 18 +- .../app/api/fetchall/[[...slugs]]/route.ts | 36 +- .../app/api/filehandlers/deletefile/route.ts | 18 +- .../filehandlers/downloadallfiles/route.ts | 22 +- .../api/filehandlers/downloadfile/route.ts | 24 +- .../app/api/filehandlers/storageload/route.ts | 42 +- .../[dataType]/[[...slugs]]/route.ts | 130 ++-- .../app/api/formsearch/attributes/route.ts | 22 +- .../app/api/formsearch/personnel/route.ts | 22 +- .../api/formsearch/personnelblock/route.ts | 38 +- frontend/app/api/formsearch/quadrats/route.ts | 22 +- frontend/app/api/formsearch/species/route.ts | 22 +- frontend/app/api/formsearch/stems/route.ts | 24 +- frontend/app/api/formsearch/trees/route.ts | 22 +- .../[dataType]/[[...slugs]]/route.ts | 12 +- frontend/app/api/hash/census/route.ts | 20 +- frontend/app/api/hash/plots/route.ts | 18 +- frontend/app/api/hash/quadrats/route.ts | 18 +- frontend/app/api/postvalidation/route.ts | 14 +- .../api/refreshviews/[view]/[schema]/route.ts | 16 +- .../rollover/[dataType]/[[...slugs]]/route.ts | 32 +- frontend/app/api/sqlload/route.ts | 58 +- frontend/app/api/sqlmonitor/route.ts | 12 +- .../api/validations/[validationType]/route.ts | 24 +- .../updatepassedvalidations/route.ts | 16 +- .../validationerrordisplay/route.ts | 24 +- .../api/validations/validationlist/route.ts | 12 +- .../app/contexts/datavalidityprovider.tsx | 16 +- .../app/contexts/listselectionprovider.tsx | 26 +- frontend/app/contexts/loadingprovider.tsx | 10 +- .../app/contexts/lockanimationcontext.tsx | 8 +- .../app/contexts/userselectionprovider.tsx | 28 +- frontend/app/error.tsx | 8 +- frontend/app/global-error.tsx | 8 +- frontend/app/layout.tsx | 24 +- frontend/app/loginfailed/page.tsx | 4 +- frontend/app/not-found.tsx | 20 +- frontend/app/providers.tsx | 12 +- frontend/components/client/clientmacros.tsx | 18 +- .../components/client/datagridcolumns.tsx | 736 +++++++++--------- frontend/components/client/entrymodal.tsx | 34 +- .../client/finalizeselectionsbutton.tsx | 26 +- .../components/client/githubfeedbackmodal.tsx | 193 +++-- frontend/components/client/loginfailure.tsx | 20 +- frontend/components/client/rollovermodal.tsx | 120 +-- .../components/client/rolloverstemsmodal.tsx | 66 +- .../alltaxonomiesviewdatagrid.tsx | 50 +- .../applications/attributesdatagrid.tsx | 50 +- .../datagrids/applications/censusdatagrid.tsx | 76 +- .../applications/personneldatagrid.tsx | 54 +- .../applications/quadratpersonneldatagrid.tsx | 80 +- .../applications/quadratsdatagrid.tsx | 58 +- .../applications/speciesdatagrid.tsx | 78 +- .../datagrids/applications/sqdatagrid.tsx | 58 +- .../stemtaxonomiesviewdatagrid.tsx | 50 +- .../viewfulltableviewdatagrid.tsx | 52 +- .../datagrids/confirmationdialog.tsx | 6 +- .../components/datagrids/datagridcommons.tsx | 229 +++--- .../components/datagrids/datagridmacros.ts | 68 +- frontend/components/datagrids/msvdatagrid.tsx | 308 ++++---- .../components/datagrids/reentrydatamodal.tsx | 86 +- .../datagrids/usedatagridcommons.ts | 42 +- .../forms/autocompletefixeddata.tsx | 22 +- .../forms/autocompletemultiselect.tsx | 18 +- .../components/forms/censusacinputform.tsx | 226 +++--- .../forms/censusinlinevalidationform.tsx | 238 +++--- .../personnelautocompletemultiselect.tsx | 28 +- frontend/components/header.tsx | 40 +- frontend/components/icons.tsx | 12 +- frontend/components/iconselection.tsx | 24 +- frontend/components/loginlogout.tsx | 50 +- .../components/processors/processcensus.tsx | 78 +- .../processors/processorhelperfunctions.tsx | 124 +-- .../components/processors/processormacros.tsx | 108 +-- .../components/processors/processspecies.tsx | 24 +- frontend/components/sidebar.tsx | 368 ++++----- .../components/themeregistry/emotioncache.tsx | 18 +- frontend/components/themeregistry/theme.ts | 34 +- .../themeregistry/themeregistry.tsx | 20 +- .../components/unauthenticatedsidebar.tsx | 48 +- .../uploadsystem/segments/uploadcomplete.tsx | 84 +- .../uploadsystem/segments/uploaderror.tsx | 16 +- .../uploadsystem/segments/uploadfireazure.tsx | 58 +- .../uploadsystem/segments/uploadfiresql.tsx | 106 +-- .../segments/uploadparsefiles.tsx | 44 +- .../segments/uploadreviewfiles.tsx | 100 +-- .../uploadsystem/segments/uploadstart.tsx | 90 +-- .../segments/uploadupdatevalidations.tsx | 34 +- .../segments/uploadvalidation.tsx | 76 +- .../components/uploadsystem/uploadparent.tsx | 108 +-- .../displayparseddatagrid.tsx | 88 +-- .../uploadsystemhelpers/dropzone.tsx | 42 +- .../uploadsystemhelpers/filelist.tsx | 42 +- .../groupedformselection.tsx | 44 +- .../uploadsystemhelpers/progressstepper.tsx | 50 +- .../uploadsystemhelpers/uploadparentmodal.tsx | 14 +- .../uploadvalidationerrordisplay.tsx | 108 +-- .../uploadsystemhelpers/viewuploadedfiles.tsx | 152 ++-- frontend/config/crypto-actions.ts | 2 +- frontend/config/datagridhelpers.ts | 98 +-- frontend/config/datamapper.ts | 94 +-- frontend/config/db.ts | 22 +- frontend/config/dynamicquerybuilder.ts | 20 +- frontend/config/fonts.ts | 10 +- frontend/config/macros.ts | 28 +- frontend/config/macros/azurestorage.ts | 18 +- frontend/config/macros/contextreducers.ts | 14 +- frontend/config/macros/dataimporter.ts | 4 +- frontend/config/macros/formdetails.ts | 96 +-- frontend/config/macros/siteconfigs.ts | 108 +-- frontend/config/macros/uploadsystemmacros.ts | 30 +- frontend/config/poolmonitor.ts | 36 +- frontend/config/primitives.ts | 66 +- .../config/sqlrdsdefinitions/orgcensusrds.ts | 34 +- .../sqlrdsdefinitions/tables/attributerds.ts | 22 +- .../sqlrdsdefinitions/tables/censusrds.ts | 6 +- .../config/sqlrdsdefinitions/tables/cmrds.ts | 2 +- .../tables/coremeasurementsrds.ts | 8 +- .../sqlrdsdefinitions/tables/familyrds.ts | 4 +- .../sqlrdsdefinitions/tables/genusrds.ts | 4 +- .../sqlrdsdefinitions/tables/personnelrds.ts | 20 +- .../sqlrdsdefinitions/tables/plotrds.ts | 4 +- .../tables/quadratpersonnelrds.ts | 6 +- .../sqlrdsdefinitions/tables/quadratrds.ts | 32 +- .../sqlrdsdefinitions/tables/referencerds.ts | 4 +- .../sqlrdsdefinitions/tables/rolesrds.ts | 4 +- .../sqlrdsdefinitions/tables/sitesrds.ts | 2 +- .../sqlrdsdefinitions/tables/speciesrds.ts | 62 +- .../sqlrdsdefinitions/tables/specimensrds.ts | 4 +- .../sqlrdsdefinitions/tables/spinvrds.ts | 2 +- .../sqlrdsdefinitions/tables/stemrds.ts | 6 +- .../sqlrdsdefinitions/tables/subquadratrds.ts | 12 +- .../sqlrdsdefinitions/tables/treerds.ts | 6 +- .../tables/valchangelogrds.ts | 6 +- .../views/alltaxonomiesviewrds.ts | 4 +- .../views/measurementssummaryviewrds.ts | 16 +- .../views/stemtaxonomiesviewrds.ts | 6 +- .../views/viewfulltableviewrds.ts | 4 +- frontend/config/styleddatagrid.ts | 64 +- frontend/config/updatecontextsfromidb.ts | 32 +- frontend/config/utils.ts | 36 +- frontend/ecosystem.config.js | 12 +- frontend/middleware.ts | 24 +- frontend/next.config.js | 10 +- frontend/playwright.config.ts | 20 +- frontend/postcss.config.js | 2 +- frontend/styles/globalloadingindicator.tsx | 36 +- frontend/styles/globals.css | 6 +- frontend/styles/rainbowicon.tsx | 38 +- frontend/styles/rainbowtext.tsx | 26 +- frontend/styles/validationtable.css | 6 +- frontend/styles/versions/acaciaversion.tsx | 30 +- frontend/tailwind.config.js | 167 ++-- frontend/tests-examples/demo-todo-app.spec.ts | 296 +++---- frontend/tests/example.spec.ts | 14 +- frontend/types/index.ts | 2 +- frontend/types/next-auth.d.ts | 8 +- frontend/utils/errorhandler.ts | 6 +- frontend/vitest.config.ts | 16 +- frontend/vitest.setup.ts | 2 +- 205 files changed, 4577 insertions(+), 4574 deletions(-) diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js index 364dac5a..60a5cd40 100644 --- a/frontend/.eslintrc.js +++ b/frontend/.eslintrc.js @@ -1,28 +1,22 @@ module.exports = { - parser: "@typescript-eslint/parser", - extends: ["next", "plugin:@typescript-eslint/recommended", "plugin:prettier/recommended"], + parser: '@typescript-eslint/parser', + extends: ['next', 'plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'], settings: { next: { - rootDir: "." + rootDir: '.' } }, - plugins: ["@typescript-eslint", "unused-imports", "prettier", "import"], + plugins: ['@typescript-eslint', 'unused-imports', 'prettier', 'import'], rules: { - "react-hooks/exhaustive-deps": "off", - semi: ["error", "always"], - "unused-imports/no-unused-imports": "error", - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-unused-vars": "off", - "unused-imports/no-unused-vars": "off", - "react-hooks/rules-of-hooks": "off", - "no-case-declarations": "off", - "prettier/prettier": "error", - "import/order": [ - "error", - { - groups: [["builtin", "external", "internal"]], - "newlines-between": "always" - } - ] + 'react-hooks/exhaustive-deps': 'off', + semi: ['error', 'always'], + 'unused-imports/no-unused-imports': 'error', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-unused-vars': 'off', + '@typescript-eslint/no-var-requires': 'off', + 'unused-imports/no-unused-vars': 'off', + 'react-hooks/rules-of-hooks': 'off', + 'no-case-declarations': 'off', + 'prettier/prettier': 'error' } }; diff --git a/frontend/.prettierrc.json b/frontend/.prettierrc.json index 271dc59f..af6398a9 100644 --- a/frontend/.prettierrc.json +++ b/frontend/.prettierrc.json @@ -1,6 +1,6 @@ { "semi": true, - "singleQuote": false, + "singleQuote": true, "trailingComma": "none", "bracketSpacing": true, "jsxBracketSameLine": false, diff --git a/frontend/__tests__/api/cmid.test.tsx b/frontend/__tests__/api/cmid.test.tsx index 0f05bc04..4b317774 100644 --- a/frontend/__tests__/api/cmid.test.tsx +++ b/frontend/__tests__/api/cmid.test.tsx @@ -1,27 +1,27 @@ -import { describe, it, expect, vi } from "vitest"; -import { GET } from "@/app/api/details/cmid/route"; -import { getConn, runQuery } from "@/components/processors/processormacros"; -import { createMocks } from "node-mocks-http"; -import { NextRequest } from "next/server"; +import { describe, it, expect, vi } from 'vitest'; +import { GET } from '@/app/api/details/cmid/route'; +import { getConn, runQuery } from '@/components/processors/processormacros'; +import { createMocks } from 'node-mocks-http'; +import { NextRequest } from 'next/server'; -vi.mock("@/components/processors/processormacros", () => ({ +vi.mock('@/components/processors/processormacros', () => ({ getConn: vi.fn(), runQuery: vi.fn() })); -describe("GET /api/details/cmid", () => { - it("should return 200 and data if query is successful", async () => { +describe('GET /api/details/cmid', () => { + it('should return 200 and data if query is successful', async () => { const mockData = [ { CoreMeasurementID: 1, - PlotName: "Plot 1", - QuadratName: "Quadrat 1", + PlotName: 'Plot 1', + QuadratName: 'Quadrat 1', PlotCensusNumber: 1, - StartDate: "2023-01-01", - EndDate: "2023-01-31", - FirstName: "John", - LastName: "Doe", - SpeciesName: "Species 1" + StartDate: '2023-01-01', + EndDate: '2023-01-31', + FirstName: 'John', + LastName: 'Doe', + SpeciesName: 'Species 1' } ]; @@ -34,8 +34,8 @@ describe("GET /api/details/cmid", () => { (runQuery as jest.Mock).mockResolvedValue(mockData); const { req, res } = createMocks({ - method: "GET", - url: "http://localhost/api/details/cmid?cmid=1&schema=test_schema" + method: 'GET', + url: 'http://localhost/api/details/cmid?cmid=1&schema=test_schema' }); const mockReq = new NextRequest(req.url); @@ -56,27 +56,27 @@ describe("GET /api/details/cmid", () => { ); }); - it("should return 500 if there is a database error", async () => { - (getConn as jest.Mock).mockRejectedValue(new Error("Database error")); + it('should return 500 if there is a database error', async () => { + (getConn as jest.Mock).mockRejectedValue(new Error('Database error')); const { req, res } = createMocks({ - method: "GET", - url: "http://localhost/api/details/cmid?cmid=1&schema=test_schema" + method: 'GET', + url: 'http://localhost/api/details/cmid?cmid=1&schema=test_schema' }); const mockReq = new NextRequest(req.url); - await expect(GET(mockReq)).rejects.toThrow("Database error"); + await expect(GET(mockReq)).rejects.toThrow('Database error'); }); - it("should return 400 if schema is not provided", async () => { + it('should return 400 if schema is not provided', async () => { const { req, res } = createMocks({ - method: "GET", - url: "http://localhost/api/details/cmid?cmid=1" + method: 'GET', + url: 'http://localhost/api/details/cmid?cmid=1' }); const mockReq = new NextRequest(req.url); - await expect(GET(mockReq)).rejects.toThrow("no schema variable provided!"); + await expect(GET(mockReq)).rejects.toThrow('no schema variable provided!'); }); }); diff --git a/frontend/__tests__/api/cmprevalidation.test.tsx b/frontend/__tests__/api/cmprevalidation.test.tsx index 49a0862e..20293687 100644 --- a/frontend/__tests__/api/cmprevalidation.test.tsx +++ b/frontend/__tests__/api/cmprevalidation.test.tsx @@ -1,17 +1,17 @@ -import { describe, it, expect, vi } from "vitest"; -import { GET } from "@/app/api/cmprevalidation/[dataType]/[[...slugs]]/route"; -import { createMocks } from "node-mocks-http"; -import { getConn, runQuery } from "@/components/processors/processormacros"; -import { HTTPResponses } from "@/config/macros"; -import { NextRequest } from "next/server"; - -vi.mock("@/components/processors/processormacros", () => ({ +import { describe, it, expect, vi } from 'vitest'; +import { GET } from '@/app/api/cmprevalidation/[dataType]/[[...slugs]]/route'; +import { createMocks } from 'node-mocks-http'; +import { getConn, runQuery } from '@/components/processors/processormacros'; +import { HTTPResponses } from '@/config/macros'; +import { NextRequest } from 'next/server'; + +vi.mock('@/components/processors/processormacros', () => ({ getConn: vi.fn(), runQuery: vi.fn() })); -describe("GET /api/cmprevalidation/[dataType]/[[...slugs]]", () => { - it("should return 412 if required tables are empty", async () => { +describe('GET /api/cmprevalidation/[dataType]/[[...slugs]]', () => { + it('should return 412 if required tables are empty', async () => { const conn = { query: vi.fn().mockResolvedValue([[]]), release: vi.fn() @@ -21,18 +21,18 @@ describe("GET /api/cmprevalidation/[dataType]/[[...slugs]]", () => { (runQuery as jest.Mock).mockResolvedValue([]); const { req } = createMocks({ - method: "GET", - url: "http://localhost/api/cmprevalidation/attributes/schema/1/1" + method: 'GET', + url: 'http://localhost/api/cmprevalidation/attributes/schema/1/1' }); const mockReq = new NextRequest(req.url); - const response = await GET(mockReq, { params: { dataType: "attributes", slugs: ["schema", "1", "1"] } }); + const response = await GET(mockReq, { params: { dataType: 'attributes', slugs: ['schema', '1', '1'] } }); expect(response.status).toBe(HTTPResponses.PRECONDITION_VALIDATION_FAILURE); }); - it("should return 200 if required tables are populated", async () => { + it('should return 200 if required tables are populated', async () => { const conn = { query: vi.fn().mockResolvedValue([[1]]), release: vi.fn() @@ -42,59 +42,59 @@ describe("GET /api/cmprevalidation/[dataType]/[[...slugs]]", () => { (runQuery as jest.Mock).mockResolvedValue([[1]]); const { req } = createMocks({ - method: "GET", - url: "http://localhost/api/cmprevalidation/attributes/schema/1/1" + method: 'GET', + url: 'http://localhost/api/cmprevalidation/attributes/schema/1/1' }); const mockReq = new NextRequest(req.url); - const response = await GET(mockReq, { params: { dataType: "attributes", slugs: ["schema", "1", "1"] } }); + const response = await GET(mockReq, { params: { dataType: 'attributes', slugs: ['schema', '1', '1'] } }); expect(response.status).toBe(HTTPResponses.OK); }); - it("should return 412 if there is a database error", async () => { - (getConn as jest.Mock).mockRejectedValue(new Error("Database error")); + it('should return 412 if there is a database error', async () => { + (getConn as jest.Mock).mockRejectedValue(new Error('Database error')); const { req } = createMocks({ - method: "GET", - url: "http://localhost/api/cmprevalidation/attributes/schema/1/1" + method: 'GET', + url: 'http://localhost/api/cmprevalidation/attributes/schema/1/1' }); const mockReq = new NextRequest(req.url); - const response = await GET(mockReq, { params: { dataType: "attributes", slugs: ["schema", "1", "1"] } }); + const response = await GET(mockReq, { params: { dataType: 'attributes', slugs: ['schema', '1', '1'] } }); expect(response.status).toBe(HTTPResponses.PRECONDITION_VALIDATION_FAILURE); }); - it("should return 400 if slugs are missing", async () => { + it('should return 400 if slugs are missing', async () => { const { req } = createMocks({ - method: "GET", - url: "http://localhost/api/cmprevalidation/attributes" + method: 'GET', + url: 'http://localhost/api/cmprevalidation/attributes' }); const mockReq = new NextRequest(req.url); try { - await GET(mockReq, { params: { dataType: "attributes", slugs: [] } }); + await GET(mockReq, { params: { dataType: 'attributes', slugs: [] } }); } catch (e) { - expect((e as Error).message).toBe("incorrect slugs provided"); + expect((e as Error).message).toBe('incorrect slugs provided'); } }); - it("should return 400 if slugs are incorrect", async () => { + it('should return 400 if slugs are incorrect', async () => { const { req } = createMocks({ - method: "GET", - url: "http://localhost/api/cmprevalidation/attributes/schema" + method: 'GET', + url: 'http://localhost/api/cmprevalidation/attributes/schema' }); const mockReq = new NextRequest(req.url); try { - await GET(mockReq, { params: { dataType: "attributes", slugs: ["schema"] } }); + await GET(mockReq, { params: { dataType: 'attributes', slugs: ['schema'] } }); } catch (e) { - expect((e as Error).message).toBe("incorrect slugs provided"); + expect((e as Error).message).toBe('incorrect slugs provided'); } }); }); diff --git a/frontend/__tests__/api/fetchall.test.tsx b/frontend/__tests__/api/fetchall.test.tsx index 4569e8f4..5ea7ff9f 100644 --- a/frontend/__tests__/api/fetchall.test.tsx +++ b/frontend/__tests__/api/fetchall.test.tsx @@ -1,94 +1,94 @@ -import { describe, it, expect, vi, beforeEach } from "vitest"; -import { GET } from "@/app/api/fetchall/[[...slugs]]/route"; -import { getConn, runQuery } from "@/components/processors/processormacros"; -import MapperFactory, { IDataMapper } from "@/config/datamapper"; -import { createMocks } from "node-mocks-http"; -import { NextRequest } from "next/server"; +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { GET } from '@/app/api/fetchall/[[...slugs]]/route'; +import { getConn, runQuery } from '@/components/processors/processormacros'; +import MapperFactory, { IDataMapper } from '@/config/datamapper'; +import { createMocks } from 'node-mocks-http'; +import { NextRequest } from 'next/server'; // Mocking getConn and runQuery functions -vi.mock("@/components/processors/processormacros", () => ({ +vi.mock('@/components/processors/processormacros', () => ({ getConn: vi.fn(), runQuery: vi.fn() })); // Mocking MapperFactory -vi.mock("@/config/datamapper", () => ({ +vi.mock('@/config/datamapper', () => ({ default: { getMapper: vi.fn() } })); -describe("GET /api/fetchall/[[...slugs]]", () => { +describe('GET /api/fetchall/[[...slugs]]', () => { beforeEach(() => { vi.clearAllMocks(); }); - it("should return 500 if schema is not provided", async () => { + it('should return 500 if schema is not provided', async () => { const { req } = createMocks({ - method: "GET", - url: "http://localhost/api/fetchall/plots" + method: 'GET', + url: 'http://localhost/api/fetchall/plots' }); const mockReq = new NextRequest(req.url); - await expect(GET(mockReq, { params: { slugs: ["plots"] } })).rejects.toThrow("Schema selection was not provided to API endpoint"); + await expect(GET(mockReq, { params: { slugs: ['plots'] } })).rejects.toThrow('Schema selection was not provided to API endpoint'); }); - it("should return 500 if fetchType is not provided", async () => { + it('should return 500 if fetchType is not provided', async () => { const { req } = createMocks({ - method: "GET", - url: "http://localhost/api/fetchall?schema=test_schema" + method: 'GET', + url: 'http://localhost/api/fetchall?schema=test_schema' }); const mockReq = new NextRequest(req.url); - await expect(GET(mockReq, { params: { slugs: [] } })).rejects.toThrow("fetchType was not correctly provided"); + await expect(GET(mockReq, { params: { slugs: [] } })).rejects.toThrow('fetchType was not correctly provided'); }); - it("should return 200 and data if query is successful", async () => { + it('should return 200 and data if query is successful', async () => { const mockConn = { release: vi.fn() }; (getConn as ReturnType).mockResolvedValue(mockConn); - const mockResults = [{ PlotID: 1, PlotName: "Plot 1" }]; + const mockResults = [{ PlotID: 1, PlotName: 'Plot 1' }]; (runQuery as ReturnType).mockResolvedValue(mockResults); const mockMapper: IDataMapper = { - mapData: vi.fn().mockReturnValue([{ plotID: 1, plotName: "Plot 1" }]), + mapData: vi.fn().mockReturnValue([{ plotID: 1, plotName: 'Plot 1' }]), demapData: vi.fn() }; (MapperFactory.getMapper as ReturnType).mockReturnValue(mockMapper); const { req } = createMocks({ - method: "GET", - url: "http://localhost/api/fetchall/plots?schema=test_schema" + method: 'GET', + url: 'http://localhost/api/fetchall/plots?schema=test_schema' }); const mockReq = new NextRequest(req.url); - const response = await GET(mockReq, { params: { slugs: ["plots"] } }); + const response = await GET(mockReq, { params: { slugs: ['plots'] } }); expect(response.status).toBe(200); const data = await response.json(); - expect(data).toEqual([{ plotID: 1, plotName: "Plot 1" }]); + expect(data).toEqual([{ plotID: 1, plotName: 'Plot 1' }]); expect(getConn).toHaveBeenCalled(); - expect(runQuery).toHaveBeenCalledWith(mockConn, expect.stringContaining("SELECT")); + expect(runQuery).toHaveBeenCalledWith(mockConn, expect.stringContaining('SELECT')); expect(mockMapper.mapData).toHaveBeenCalledWith(mockResults); expect(mockConn.release).toHaveBeenCalled(); }); - it("should return 500 if there is a database error", async () => { + it('should return 500 if there is a database error', async () => { const mockConn = { release: vi.fn() }; (getConn as ReturnType).mockResolvedValue(mockConn); - (runQuery as ReturnType).mockRejectedValue(new Error("Database error")); + (runQuery as ReturnType).mockRejectedValue(new Error('Database error')); const { req } = createMocks({ - method: "GET", - url: "http://localhost/api/fetchall/plots?schema=test_schema" + method: 'GET', + url: 'http://localhost/api/fetchall/plots?schema=test_schema' }); const mockReq = new NextRequest(req.url); - await expect(GET(mockReq, { params: { slugs: ["plots"] } })).rejects.toThrow("Call failed"); + await expect(GET(mockReq, { params: { slugs: ['plots'] } })).rejects.toThrow('Call failed'); expect(getConn).toHaveBeenCalled(); - expect(runQuery).toHaveBeenCalledWith(mockConn, expect.stringContaining("SELECT")); + expect(runQuery).toHaveBeenCalledWith(mockConn, expect.stringContaining('SELECT')); expect(mockConn.release).toHaveBeenCalled(); }); }); diff --git a/frontend/__tests__/api/filehandlers/deletefile.test.tsx b/frontend/__tests__/api/filehandlers/deletefile.test.tsx index dac1e737..1bb7a42a 100644 --- a/frontend/__tests__/api/filehandlers/deletefile.test.tsx +++ b/frontend/__tests__/api/filehandlers/deletefile.test.tsx @@ -1,22 +1,22 @@ -import { describe, it, expect, vi, beforeEach } from "vitest"; -import { DELETE } from "@/app/api/filehandlers/deletefile/route"; -import { getContainerClient } from "@/config/macros/azurestorage"; -import { createMocks } from "node-mocks-http"; -import { NextRequest } from "next/server"; +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { DELETE } from '@/app/api/filehandlers/deletefile/route'; +import { getContainerClient } from '@/config/macros/azurestorage'; +import { createMocks } from 'node-mocks-http'; +import { NextRequest } from 'next/server'; -vi.mock("@/config/macros/azurestorage", () => ({ +vi.mock('@/config/macros/azurestorage', () => ({ getContainerClient: vi.fn() })); -describe("DELETE /api/filehandlers/deletefile", () => { +describe('DELETE /api/filehandlers/deletefile', () => { beforeEach(() => { vi.clearAllMocks(); }); - it("should return 400 if container name or filename is missing", async () => { + it('should return 400 if container name or filename is missing', async () => { const { req } = createMocks({ - method: "DELETE", - url: "http://localhost/api/filehandlers/deletefile" + method: 'DELETE', + url: 'http://localhost/api/filehandlers/deletefile' }); const mockReq = new NextRequest(req.url); @@ -24,15 +24,15 @@ describe("DELETE /api/filehandlers/deletefile", () => { const response = await DELETE(mockReq); expect(response.status).toBe(400); const data = await response.text(); - expect(data).toBe("Container name and filename are required"); + expect(data).toBe('Container name and filename are required'); }); - it("should return 400 if container client creation fails", async () => { + it('should return 400 if container client creation fails', async () => { (getContainerClient as ReturnType).mockResolvedValue(null); const { req } = createMocks({ - method: "DELETE", - url: "http://localhost/api/filehandlers/deletefile?container=testContainer&filename=testFile" + method: 'DELETE', + url: 'http://localhost/api/filehandlers/deletefile?container=testContainer&filename=testFile' }); const mockReq = new NextRequest(req.url); @@ -40,10 +40,10 @@ describe("DELETE /api/filehandlers/deletefile", () => { const response = await DELETE(mockReq); expect(response.status).toBe(400); const data = await response.text(); - expect(data).toBe("Container name and filename are required"); + expect(data).toBe('Container name and filename are required'); }); - it("should return 200 and delete the file if successful", async () => { + it('should return 200 and delete the file if successful', async () => { const mockBlobClient = { delete: vi.fn().mockResolvedValue({}) }; @@ -54,8 +54,8 @@ describe("DELETE /api/filehandlers/deletefile", () => { (getContainerClient as ReturnType).mockResolvedValue(mockContainerClient); const { req } = createMocks({ - method: "DELETE", - url: "http://localhost/api/filehandlers/deletefile?container=testContainer&filename=testFile" + method: 'DELETE', + url: 'http://localhost/api/filehandlers/deletefile?container=testContainer&filename=testFile' }); const mockReq = new NextRequest(req.url); @@ -63,16 +63,16 @@ describe("DELETE /api/filehandlers/deletefile", () => { const response = await DELETE(mockReq); expect(response.status).toBe(200); const data = await response.text(); - expect(data).toBe("File deleted successfully"); + expect(data).toBe('File deleted successfully'); expect(mockBlobClient.delete).toHaveBeenCalled(); }); - it("should return 500 if there is an error", async () => { - (getContainerClient as ReturnType).mockRejectedValue(new Error("Test error")); + it('should return 500 if there is an error', async () => { + (getContainerClient as ReturnType).mockRejectedValue(new Error('Test error')); const { req } = createMocks({ - method: "DELETE", - url: "http://localhost/api/filehandlers/deletefile?container=testContainer&filename=testFile" + method: 'DELETE', + url: 'http://localhost/api/filehandlers/deletefile?container=testContainer&filename=testFile' }); const mockReq = new NextRequest(req.url); @@ -80,6 +80,6 @@ describe("DELETE /api/filehandlers/deletefile", () => { const response = await DELETE(mockReq); expect(response.status).toBe(500); const data = await response.text(); - expect(data).toBe("Test error"); + expect(data).toBe('Test error'); }); }); diff --git a/frontend/__tests__/api/filehandlers/downloadallfiles.test.tsx b/frontend/__tests__/api/filehandlers/downloadallfiles.test.tsx index d6ad1416..93d09db0 100644 --- a/frontend/__tests__/api/filehandlers/downloadallfiles.test.tsx +++ b/frontend/__tests__/api/filehandlers/downloadallfiles.test.tsx @@ -1,22 +1,22 @@ -import { describe, it, expect, vi, beforeEach } from "vitest"; -import { GET } from "@/app/api/filehandlers/downloadallfiles/route"; -import { getContainerClient } from "@/config/macros/azurestorage"; -import { createMocks } from "node-mocks-http"; -import { NextRequest } from "next/server"; +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { GET } from '@/app/api/filehandlers/downloadallfiles/route'; +import { getContainerClient } from '@/config/macros/azurestorage'; +import { createMocks } from 'node-mocks-http'; +import { NextRequest } from 'next/server'; -vi.mock("@/config/macros/azurestorage", () => ({ +vi.mock('@/config/macros/azurestorage', () => ({ getContainerClient: vi.fn() })); -describe("GET /api/filehandlers/downloadallfiles", () => { +describe('GET /api/filehandlers/downloadallfiles', () => { beforeEach(() => { vi.clearAllMocks(); }); - it("should return 400 if plot or census is not provided", async () => { + it('should return 400 if plot or census is not provided', async () => { const { req } = createMocks({ - method: "GET", - url: "http://localhost/api/filehandlers/downloadallfiles" + method: 'GET', + url: 'http://localhost/api/filehandlers/downloadallfiles' }); const mockReq = new NextRequest(req.url); @@ -24,15 +24,15 @@ describe("GET /api/filehandlers/downloadallfiles", () => { const response = await GET(mockReq); expect(response.status).toBe(400); const data = await response.text(); - expect(data).toBe("Both plot and census parameters are required"); + expect(data).toBe('Both plot and census parameters are required'); }); - it("should return 400 if container client creation fails", async () => { + it('should return 400 if container client creation fails', async () => { (getContainerClient as ReturnType).mockResolvedValue(null); const { req } = createMocks({ - method: "GET", - url: "http://localhost/api/filehandlers/downloadallfiles?plot=testPlot&census=testCensus" + method: 'GET', + url: 'http://localhost/api/filehandlers/downloadallfiles?plot=testPlot&census=testCensus' }); const mockReq = new NextRequest(req.url); @@ -40,18 +40,18 @@ describe("GET /api/filehandlers/downloadallfiles", () => { const response = await GET(mockReq); expect(response.status).toBe(400); const data = await response.json(); - expect(data.statusText).toBe("Container client creation error"); + expect(data.statusText).toBe('Container client creation error'); }); - it("should return 200 and list of blobs if successful", async () => { + it('should return 200 and list of blobs if successful', async () => { const mockContainerClient = { listBlobsFlat: vi.fn().mockImplementation(function* () { yield { - name: "testBlob", + name: 'testBlob', metadata: { - user: "testUser", - FormType: "testFormType", - FileErrorState: JSON.stringify([{ stemtag: "testStemtag", tag: "testTag", validationErrorID: 1 }]) + user: 'testUser', + FormType: 'testFormType', + FileErrorState: JSON.stringify([{ stemtag: 'testStemtag', tag: 'testTag', validationErrorID: 1 }]) }, properties: { lastModified: new Date() @@ -63,8 +63,8 @@ describe("GET /api/filehandlers/downloadallfiles", () => { (getContainerClient as ReturnType).mockResolvedValue(mockContainerClient); const { req } = createMocks({ - method: "GET", - url: "http://localhost/api/filehandlers/downloadallfiles?plot=testPlot&census=testCensus" + method: 'GET', + url: 'http://localhost/api/filehandlers/downloadallfiles?plot=testPlot&census=testCensus' }); const mockReq = new NextRequest(req.url); @@ -72,30 +72,30 @@ describe("GET /api/filehandlers/downloadallfiles", () => { const response = await GET(mockReq); expect(response.status).toBe(200); const data = await response.json(); - expect(data.responseMessage).toBe("List of files"); + expect(data.responseMessage).toBe('List of files'); expect(data.blobData).toHaveLength(1); expect(data.blobData[0]).toEqual({ key: 1, - name: "testBlob", - user: "testUser", - formType: "testFormType", - fileErrors: [{ stemtag: "testStemtag", tag: "testTag", validationErrorID: 1 }], + name: 'testBlob', + user: 'testUser', + formType: 'testFormType', + fileErrors: [{ stemtag: 'testStemtag', tag: 'testTag', validationErrorID: 1 }], date: expect.any(String) // Date will be serialized to a string }); }); - it("should return 400 if there is an error in blob listing", async () => { + it('should return 400 if there is an error in blob listing', async () => { const mockContainerClient = { listBlobsFlat: vi.fn().mockImplementation(() => { - throw new Error("Blob listing error"); + throw new Error('Blob listing error'); }) }; (getContainerClient as ReturnType).mockResolvedValue(mockContainerClient); const { req } = createMocks({ - method: "GET", - url: "http://localhost/api/filehandlers/downloadallfiles?plot=testPlot&census=testCensus" + method: 'GET', + url: 'http://localhost/api/filehandlers/downloadallfiles?plot=testPlot&census=testCensus' }); const mockReq = new NextRequest(req.url); @@ -103,6 +103,6 @@ describe("GET /api/filehandlers/downloadallfiles", () => { const response = await GET(mockReq); expect(response.status).toBe(400); const data = await response.json(); - expect(data.message).toBe("Blob listing error"); + expect(data.message).toBe('Blob listing error'); }); }); diff --git a/frontend/__tests__/api/filehandlers/downloadfile.test.tsx b/frontend/__tests__/api/filehandlers/downloadfile.test.tsx index 88c75a5c..6ac52b51 100644 --- a/frontend/__tests__/api/filehandlers/downloadfile.test.tsx +++ b/frontend/__tests__/api/filehandlers/downloadfile.test.tsx @@ -1,12 +1,12 @@ -import { describe, it, expect, vi, beforeEach } from "vitest"; -import { GET } from "@/app/api/filehandlers/downloadfile/route"; -import { getContainerClient } from "@/config/macros/azurestorage"; -import { createMocks } from "node-mocks-http"; -import { NextRequest } from "next/server"; -import { BlobServiceClient, generateBlobSASQueryParameters, StorageSharedKeyCredential } from "@azure/storage-blob"; - -vi.mock("@azure/storage-blob", async () => { - const actual = await vi.importActual("@azure/storage-blob"); +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { GET } from '@/app/api/filehandlers/downloadfile/route'; +import { getContainerClient } from '@/config/macros/azurestorage'; +import { createMocks } from 'node-mocks-http'; +import { NextRequest } from 'next/server'; +import { BlobServiceClient, generateBlobSASQueryParameters, StorageSharedKeyCredential } from '@azure/storage-blob'; + +vi.mock('@azure/storage-blob', async () => { + const actual = await vi.importActual('@azure/storage-blob'); return { ...actual, BlobServiceClient: { @@ -16,19 +16,19 @@ vi.mock("@azure/storage-blob", async () => { }; }); -vi.mock("@/config/macros/azurestorage", () => ({ +vi.mock('@/config/macros/azurestorage', () => ({ getContainerClient: vi.fn() })); -describe("GET /api/filehandlers/downloadfile", () => { +describe('GET /api/filehandlers/downloadfile', () => { beforeEach(() => { vi.clearAllMocks(); }); - it("should return 400 if container name, filename, or storage connection string is missing", async () => { + it('should return 400 if container name, filename, or storage connection string is missing', async () => { const { req } = createMocks({ - method: "GET", - url: "http://localhost/api/filehandlers/downloadfile" + method: 'GET', + url: 'http://localhost/api/filehandlers/downloadfile' }); const mockReq = new NextRequest(req.url); @@ -36,16 +36,16 @@ describe("GET /api/filehandlers/downloadfile", () => { const response = await GET(mockReq); expect(response.status).toBe(400); const data = await response.text(); - expect(data).toBe("Container name, filename, and storage connection string are required"); + expect(data).toBe('Container name, filename, and storage connection string are required'); }); - it("should return 400 if container client creation fails", async () => { - process.env.AZURE_STORAGE_CONNECTION_STRING = "test-connection-string"; + it('should return 400 if container client creation fails', async () => { + process.env.AZURE_STORAGE_CONNECTION_STRING = 'test-connection-string'; (getContainerClient as ReturnType).mockResolvedValue(null); const { req } = createMocks({ - method: "GET", - url: "http://localhost/api/filehandlers/downloadfile?container=testContainer&filename=testFile" + method: 'GET', + url: 'http://localhost/api/filehandlers/downloadfile?container=testContainer&filename=testFile' }); const mockReq = new NextRequest(req.url); @@ -53,31 +53,31 @@ describe("GET /api/filehandlers/downloadfile", () => { const response = await GET(mockReq); expect(response.status).toBe(400); const data = await response.text(); - expect(data).toBe("Failed to get container client"); + expect(data).toBe('Failed to get container client'); }); - it("should return 200 and SAS token URL if successful", async () => { - process.env.AZURE_STORAGE_CONNECTION_STRING = "test-connection-string"; + it('should return 200 and SAS token URL if successful', async () => { + process.env.AZURE_STORAGE_CONNECTION_STRING = 'test-connection-string'; const mockContainerClient = { getBlobClient: vi.fn().mockReturnValue({ - url: "https://testaccount.blob.core.windows.net/testcontainer/testblob" + url: 'https://testaccount.blob.core.windows.net/testcontainer/testblob' }) }; (getContainerClient as ReturnType).mockResolvedValue(mockContainerClient); (BlobServiceClient.fromConnectionString as ReturnType).mockReturnValue({ - credential: new StorageSharedKeyCredential("testaccount", "testkey") + credential: new StorageSharedKeyCredential('testaccount', 'testkey') }); (generateBlobSASQueryParameters as ReturnType).mockReturnValue({ - toString: () => "sastoken" + toString: () => 'sastoken' }); const { req } = createMocks({ - method: "GET", - url: "http://localhost/api/filehandlers/downloadfile?container=testContainer&filename=testFile" + method: 'GET', + url: 'http://localhost/api/filehandlers/downloadfile?container=testContainer&filename=testFile' }); const mockReq = new NextRequest(req.url); @@ -85,17 +85,17 @@ describe("GET /api/filehandlers/downloadfile", () => { const response = await GET(mockReq); expect(response.status).toBe(200); const data = await response.json(); - expect(data.url).toBe("https://testaccount.blob.core.windows.net/testcontainer/testblob?sastoken"); + expect(data.url).toBe('https://testaccount.blob.core.windows.net/testcontainer/testblob?sastoken'); }); - it("should return 500 if there is an error", async () => { - process.env.AZURE_STORAGE_CONNECTION_STRING = "test-connection-string"; + it('should return 500 if there is an error', async () => { + process.env.AZURE_STORAGE_CONNECTION_STRING = 'test-connection-string'; - (getContainerClient as ReturnType).mockRejectedValue(new Error("Test error")); + (getContainerClient as ReturnType).mockRejectedValue(new Error('Test error')); const { req } = createMocks({ - method: "GET", - url: "http://localhost/api/filehandlers/downloadfile?container=testContainer&filename=testFile" + method: 'GET', + url: 'http://localhost/api/filehandlers/downloadfile?container=testContainer&filename=testFile' }); const mockReq = new NextRequest(req.url); @@ -103,6 +103,6 @@ describe("GET /api/filehandlers/downloadfile", () => { const response = await GET(mockReq); expect(response.status).toBe(500); const data = await response.text(); - expect(data).toBe("Test error"); + expect(data).toBe('Test error'); }); }); diff --git a/frontend/__tests__/api/filehandlers/storageload.test.tsx b/frontend/__tests__/api/filehandlers/storageload.test.tsx index e9461d53..ad11453f 100644 --- a/frontend/__tests__/api/filehandlers/storageload.test.tsx +++ b/frontend/__tests__/api/filehandlers/storageload.test.tsx @@ -1,31 +1,31 @@ -import { describe, it, expect, vi, beforeEach } from "vitest"; -import { POST } from "@/app/api/filehandlers/storageload/route"; -import { getContainerClient, uploadValidFileAsBuffer } from "@/config/macros/azurestorage"; -import { createMocks } from "node-mocks-http"; -import { NextRequest } from "next/server"; +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { POST } from '@/app/api/filehandlers/storageload/route'; +import { getContainerClient, uploadValidFileAsBuffer } from '@/config/macros/azurestorage'; +import { createMocks } from 'node-mocks-http'; +import { NextRequest } from 'next/server'; -vi.mock("@/config/macros/azurestorage", () => ({ +vi.mock('@/config/macros/azurestorage', () => ({ getContainerClient: vi.fn(), uploadValidFileAsBuffer: vi.fn() })); -describe.skip("POST /api/filehandlers/storageload", () => { +describe.skip('POST /api/filehandlers/storageload', () => { beforeEach(() => { vi.clearAllMocks(); }); const createMockRequest = (url: string, formData: FormData) => { const { req } = createMocks({ - method: "POST", + method: 'POST', url: url, headers: { - "Content-Type": "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW" + 'Content-Type': 'multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' } }); - if (formData.get("file") === null) { - console.log("createMockRequest: received empty formData: ", formData); - return new NextRequest(req.url!, { method: "POST" }); + if (formData.get('file') === null) { + console.log('createMockRequest: received empty formData: ', formData); + return new NextRequest(req.url!, { method: 'POST' }); } req.formData = async () => formData; @@ -36,96 +36,96 @@ describe.skip("POST /api/filehandlers/storageload", () => { const body = `------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name="file"; filename="testfile.txt"\r\nContent-Type: text/plain\r\n\r\ntest content\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--`; - return new NextRequest(req.url!, { method: "POST", headers, body }); + return new NextRequest(req.url!, { method: 'POST', headers, body }); }; - it("should return 500 if container client creation fails", async () => { - (getContainerClient as ReturnType).mockRejectedValue(new Error("Test error")); + it('should return 500 if container client creation fails', async () => { + (getContainerClient as ReturnType).mockRejectedValue(new Error('Test error')); const formData = new FormData(); - formData.append("file", new File(["test content"], "testfile.txt")); + formData.append('file', new File(['test content'], 'testfile.txt')); const mockReq = createMockRequest( - "http://localhost/api/filehandlers/storageload?fileName=testfile.txt&plot=testplot&census=testcensus&user=testuser&formType=testform", + 'http://localhost/api/filehandlers/storageload?fileName=testfile.txt&plot=testplot&census=testcensus&user=testuser&formType=testform', formData ); const response = await POST(mockReq); expect(response.status).toBe(500); const data = await response.json(); - expect(data.responseMessage).toBe("Error getting container client."); - expect(data.error).toBe("Test error"); + expect(data.responseMessage).toBe('Error getting container client.'); + expect(data.error).toBe('Test error'); }); - it("should return 500 if file upload fails", async () => { + it('should return 500 if file upload fails', async () => { (getContainerClient as ReturnType).mockResolvedValue({}); - (uploadValidFileAsBuffer as ReturnType).mockRejectedValue(new Error("Upload error")); + (uploadValidFileAsBuffer as ReturnType).mockRejectedValue(new Error('Upload error')); const formData = new FormData(); - formData.append("file", new File(["test content"], "testfile.txt")); + formData.append('file', new File(['test content'], 'testfile.txt')); const mockReq = createMockRequest( - "http://localhost/api/filehandlers/storageload?fileName=testfile.txt&plot=testplot&census=testcensus&user=testuser&formType=testform", + 'http://localhost/api/filehandlers/storageload?fileName=testfile.txt&plot=testplot&census=testcensus&user=testuser&formType=testform', formData ); const response = await POST(mockReq); expect(response.status).toBe(500); const data = await response.json(); - expect(data.responseMessage).toBe("File Processing error"); - expect(data.error).toBe("Upload error"); + expect(data.responseMessage).toBe('File Processing error'); + expect(data.error).toBe('Upload error'); }); - it("should return 200 if file upload is successful", async () => { - const mockUploadResponse = { requestId: "12345", _response: { status: 200 } }; + it('should return 200 if file upload is successful', async () => { + const mockUploadResponse = { requestId: '12345', _response: { status: 200 } }; (getContainerClient as ReturnType).mockResolvedValue({}); (uploadValidFileAsBuffer as ReturnType).mockResolvedValue(mockUploadResponse); const formData = new FormData(); - formData.append("file", new File(["test content"], "testfile.txt")); + formData.append('file', new File(['test content'], 'testfile.txt')); const mockReq = createMockRequest( - "http://localhost/api/filehandlers/storageload?fileName=testfile.txt&plot=testplot&census=testcensus&user=testuser&formType=testform", + 'http://localhost/api/filehandlers/storageload?fileName=testfile.txt&plot=testplot&census=testcensus&user=testuser&formType=testform', formData ); const response = await POST(mockReq); expect(response.status).toBe(200); const data = await response.json(); - expect(data.message).toBe("Insert to Azure Storage successful"); + expect(data.message).toBe('Insert to Azure Storage successful'); }); - it("should return 400 if file is missing", async () => { + it('should return 400 if file is missing', async () => { const formData = new FormData(); - console.log("test formData: ", formData); + console.log('test formData: ', formData); const mockReq = createMockRequest( - "http://localhost/api/filehandlers/storageload?fileName=testfile.txt&plot=testplot&census=testcensus&user=testuser&formType=testform", + 'http://localhost/api/filehandlers/storageload?fileName=testfile.txt&plot=testplot&census=testcensus&user=testuser&formType=testform', formData ); const response = await POST(mockReq); expect(response.status).toBe(400); const data = await response.text(); - expect(data).toBe("File is required"); + expect(data).toBe('File is required'); }); - it("should return 500 for unknown errors", async () => { + it('should return 500 for unknown errors', async () => { (getContainerClient as ReturnType).mockResolvedValue({}); - (uploadValidFileAsBuffer as ReturnType).mockRejectedValue("Unknown error"); + (uploadValidFileAsBuffer as ReturnType).mockRejectedValue('Unknown error'); const formData = new FormData(); - formData.append("file", new File(["test content"], "testfile.txt")); + formData.append('file', new File(['test content'], 'testfile.txt')); const mockReq = createMockRequest( - "http://localhost/api/filehandlers/storageload?fileName=testfile.txt&plot=testplot&census=testcensus&user=testuser&formType=testform", + 'http://localhost/api/filehandlers/storageload?fileName=testfile.txt&plot=testplot&census=testcensus&user=testuser&formType=testform', formData ); const response = await POST(mockReq); expect(response.status).toBe(500); const data = await response.json(); - expect(data.responseMessage).toBe("File Processing error"); - expect(data.error).toBe("Unknown error"); + expect(data.responseMessage).toBe('File Processing error'); + expect(data.error).toBe('Unknown error'); }); }); diff --git a/frontend/__tests__/loginpage.test.tsx b/frontend/__tests__/loginpage.test.tsx index 568fb9ff..8c3b418e 100644 --- a/frontend/__tests__/loginpage.test.tsx +++ b/frontend/__tests__/loginpage.test.tsx @@ -1,49 +1,49 @@ // loginPage.test.tsx -import { render, screen } from "@testing-library/react"; -import { describe, it, vi, beforeEach, Mock, expect } from "vitest"; -import LoginPage from "@/app/(login)/login/page"; -import { useSession } from "next-auth/react"; -import { redirect } from "next/navigation"; -import "@testing-library/jest-dom/vitest"; +import { render, screen } from '@testing-library/react'; +import { describe, it, vi, beforeEach, Mock, expect } from 'vitest'; +import LoginPage from '@/app/(login)/login/page'; +import { useSession } from 'next-auth/react'; +import { redirect } from 'next/navigation'; +import '@testing-library/jest-dom/vitest'; // Mock the useSession hook and next/navigation functions -vi.mock("next-auth/react", () => ({ +vi.mock('next-auth/react', () => ({ useSession: vi.fn() })); -vi.mock("next/navigation", () => ({ +vi.mock('next/navigation', () => ({ redirect: vi.fn() })); // Mock the UnauthenticatedSidebar component -vi.mock("@/components/unauthenticatedsidebar", () => ({ +vi.mock('@/components/unauthenticatedsidebar', () => ({ default: () =>
Unauthenticated Sidebar
})); -describe("LoginPage Component", () => { +describe('LoginPage Component', () => { beforeEach(() => { // Reset mocks before each test vi.clearAllMocks(); }); - it("renders the unauthenticated sidebar if the user is unauthenticated", () => { + it('renders the unauthenticated sidebar if the user is unauthenticated', () => { // Mock unauthenticated status - (useSession as Mock).mockReturnValue({ data: null, status: "unauthenticated" }); + (useSession as Mock).mockReturnValue({ data: null, status: 'unauthenticated' }); render(); // Assert that the sidebar is present and visible - expect(screen.getByTestId("unauthenticated-sidebar")).toBeInTheDocument(); + expect(screen.getByTestId('unauthenticated-sidebar')).toBeInTheDocument(); }); - it("redirects to dashboard if the user is authenticated", () => { + it('redirects to dashboard if the user is authenticated', () => { // Mock authenticated status - (useSession as Mock).mockReturnValue({ data: { user: {} }, status: "authenticated" }); + (useSession as Mock).mockReturnValue({ data: { user: {} }, status: 'authenticated' }); render(); // Assert that redirect was called to navigate to the dashboard - expect(redirect).toHaveBeenCalledWith("/dashboard"); + expect(redirect).toHaveBeenCalledWith('/dashboard'); }); }); diff --git a/frontend/__tests__/rollovermodal.test.tsx b/frontend/__tests__/rollovermodal.test.tsx index 71387ec5..e176658e 100644 --- a/frontend/__tests__/rollovermodal.test.tsx +++ b/frontend/__tests__/rollovermodal.test.tsx @@ -1,16 +1,16 @@ -import RolloverModal from "@/components/client/rollovermodal"; -import { render, screen, fireEvent, waitFor } from "@testing-library/react"; -import { describe, it, expect, vi, beforeEach } from "vitest"; +import RolloverModal from '@/components/client/rollovermodal'; +import { render, screen, fireEvent, waitFor } from '@testing-library/react'; +import { describe, it, expect, vi, beforeEach } from 'vitest'; // Mock the fetch API global.fetch = vi.fn(); // Mock contexts -vi.mock("@/app/contexts/userselectionprovider", () => ({ - useSiteContext: () => ({ schemaName: "testSchema" }), +vi.mock('@/app/contexts/userselectionprovider', () => ({ + useSiteContext: () => ({ schemaName: 'testSchema' }), usePlotContext: () => ({ plotID: 1 }) })); -vi.mock("@/app/contexts/listselectionprovider", () => ({ +vi.mock('@/app/contexts/listselectionprovider', () => ({ useOrgCensusListContext: () => [ { plotCensusNumber: 1, dateRanges: [{ censusID: 1 }] }, { plotCensusNumber: 2, dateRanges: [{ censusID: 2 }] } @@ -19,55 +19,55 @@ vi.mock("@/app/contexts/listselectionprovider", () => ({ // Mock Data const previousPersonnel = [ - { personnelID: 1, name: "Person 1" }, - { personnelID: 2, name: "Person 2" } + { personnelID: 1, name: 'Person 1' }, + { personnelID: 2, name: 'Person 2' } ]; const previousQuadrats = [ - { quadratID: 1, name: "Quadrat 1" }, - { quadratID: 2, name: "Quadrat 2" } + { quadratID: 1, name: 'Quadrat 1' }, + { quadratID: 2, name: 'Quadrat 2' } ]; -describe.skip("RolloverModal Component", () => { +describe.skip('RolloverModal Component', () => { const setup = (props = {}) => render(); beforeEach(() => { (global.fetch as jest.Mock).mockClear(); (global.fetch as jest.Mock).mockImplementation((url: string) => { - if (url.includes("/fetchall/personnel/")) { + if (url.includes('/fetchall/personnel/')) { return Promise.resolve({ status: 200, json: () => Promise.resolve(previousPersonnel) }); } - if (url.includes("/fetchall/quadrats/")) { + if (url.includes('/fetchall/quadrats/')) { return Promise.resolve({ status: 200, json: () => Promise.resolve(previousQuadrats) }); } - if (url.includes("/cmprevalidation/personnel/")) { + if (url.includes('/cmprevalidation/personnel/')) { return Promise.resolve({ status: 200 }); } - if (url.includes("/cmprevalidation/quadrats/")) { + if (url.includes('/cmprevalidation/quadrats/')) { return Promise.resolve({ status: 200 }); } - return Promise.reject(new Error("Unknown API call")); + return Promise.reject(new Error('Unknown API call')); }); }); - it("should open modal and display title", async () => { + it('should open modal and display title', async () => { setup(); await waitFor(() => { expect(screen.getByText(/Rollover Census Data/i)).toBeInTheDocument(); }); }); - it("should show error if no checkbox is selected and confirm is pressed", async () => { + it('should show error if no checkbox is selected and confirm is pressed', async () => { setup(); fireEvent.click(screen.getByText(/Confirm/i)); await waitFor(() => { @@ -75,7 +75,7 @@ describe.skip("RolloverModal Component", () => { }); }); - it("should allow selecting and confirming personnel rollover", async () => { + it('should allow selecting and confirming personnel rollover', async () => { setup(); await waitFor(() => { fireEvent.click(screen.getByLabelText(/Roll over personnel data/i)); @@ -86,7 +86,7 @@ describe.skip("RolloverModal Component", () => { }); }); - it("should allow selecting and confirming quadrats rollover", async () => { + it('should allow selecting and confirming quadrats rollover', async () => { setup(); await waitFor(() => { fireEvent.click(screen.getByLabelText(/Roll over quadrats data/i)); @@ -97,7 +97,7 @@ describe.skip("RolloverModal Component", () => { }); }); - it("should allow selecting and confirming both personnel and quadrats rollover", async () => { + it('should allow selecting and confirming both personnel and quadrats rollover', async () => { setup(); await waitFor(() => { fireEvent.click(screen.getByLabelText(/Roll over personnel data/i)); @@ -109,7 +109,7 @@ describe.skip("RolloverModal Component", () => { }); }); - it("should allow customizing personnel selection", async () => { + it('should allow customizing personnel selection', async () => { setup(); await waitFor(() => { fireEvent.click(screen.getByLabelText(/Roll over personnel data/i)); @@ -119,7 +119,7 @@ describe.skip("RolloverModal Component", () => { expect(screen.getByText(/Person 2/i)).toBeInTheDocument(); }); - it("should allow customizing quadrats selection", async () => { + it('should allow customizing quadrats selection', async () => { setup(); await waitFor(() => { fireEvent.click(screen.getByLabelText(/Roll over quadrats data/i)); @@ -129,7 +129,7 @@ describe.skip("RolloverModal Component", () => { expect(screen.getByText(/Quadrat 2/i)).toBeInTheDocument(); }); - it("should confirm no rollover for personnel", async () => { + it('should confirm no rollover for personnel', async () => { setup(); fireEvent.mouseDown(screen.getByLabelText(/Do not roll over any Personnel data/i)); fireEvent.click(screen.getByText(/Confirm No Rollover/i)); @@ -139,7 +139,7 @@ describe.skip("RolloverModal Component", () => { }); }); - it("should confirm no rollover for quadrats", async () => { + it('should confirm no rollover for quadrats', async () => { setup(); fireEvent.mouseDown(screen.getByLabelText(/Do not roll over any Quadrats data/i)); fireEvent.click(screen.getByText(/Confirm No Rollover/i)); @@ -149,8 +149,8 @@ describe.skip("RolloverModal Component", () => { }); }); - it("should handle error during fetch data", async () => { - (global.fetch as jest.Mock).mockImplementationOnce(() => Promise.reject(new Error("Failed to fetch"))); + it('should handle error during fetch data', async () => { + (global.fetch as jest.Mock).mockImplementationOnce(() => Promise.reject(new Error('Failed to fetch'))); setup(); await waitFor(() => { expect(screen.getByText(/Failed to fetch previous data. Please try again/i)).toBeInTheDocument(); diff --git a/frontend/app/(hub)/dashboard/error.tsx b/frontend/app/(hub)/dashboard/error.tsx index 449b832e..8f3070dc 100644 --- a/frontend/app/(hub)/dashboard/error.tsx +++ b/frontend/app/(hub)/dashboard/error.tsx @@ -1,7 +1,7 @@ -"use client"; +'use client'; -import React, { useEffect } from "react"; -import { Box, Typography, Button } from "@mui/joy"; +import React, { useEffect } from 'react'; +import { Box, Typography, Button } from '@mui/joy'; const ErrorPage = ({ error, reset }: { error: Error; reset: () => void }) => { useEffect(() => { @@ -12,7 +12,7 @@ const ErrorPage = ({ error, reset }: { error: Error; reset: () => void }) => { }, [reset]); return ( - + Something went wrong - Dashboard {error.message} Retrying in 5 seconds... diff --git a/frontend/app/(hub)/dashboard/page.tsx b/frontend/app/(hub)/dashboard/page.tsx index 4569366a..416516f1 100644 --- a/frontend/app/(hub)/dashboard/page.tsx +++ b/frontend/app/(hub)/dashboard/page.tsx @@ -1,41 +1,41 @@ -"use client"; +'use client'; -import { Box, Card, CardContent, Chip, Divider, List, ListItem, ListItemContent, ListSubheader, Stack, Tooltip, Typography } from "@mui/joy"; -import HelpOutlineOutlinedIcon from "@mui/icons-material/HelpOutlineOutlined"; -import { useLockAnimation } from "@/app/contexts/lockanimationcontext"; +import { Box, Card, CardContent, Chip, Divider, List, ListItem, ListItemContent, ListSubheader, Stack, Tooltip, Typography } from '@mui/joy'; +import HelpOutlineOutlinedIcon from '@mui/icons-material/HelpOutlineOutlined'; +import { useLockAnimation } from '@/app/contexts/lockanimationcontext'; export default function DashboardPage() { - const { triggerPulse } = useLockAnimation(); + const { triggerPulse, isPulsing } = useLockAnimation(); const _attributeNote = - "NOTE: If a code can be used for more than one status (e.g. The code “L” for a leaning tree, could\n" + - "apply to either a dead or alive stem), or if a code does not indicate any of the above status\n" + - "options, the status column should be left blank."; + 'NOTE: If a code can be used for more than one status (e.g. The code “L” for a leaning tree, could\n' + + 'apply to either a dead or alive stem), or if a code does not indicate any of the above status\n' + + 'options, the status column should be left blank.'; const _quadratNote = - "NOTE: The x and y coordinates (“startx” and “starty”) refer to the distance in meters between\n" + - "the quadrat under question and lowest, left-most corner of the entire plot (or\n" + - "wherever your plot origin, or 0,0 coordinates are)."; + 'NOTE: The x and y coordinates (“startx” and “starty”) refer to the distance in meters between\n' + + 'the quadrat under question and lowest, left-most corner of the entire plot (or\n' + + 'wherever your plot origin, or 0,0 coordinates are).'; const _censusNote1 = - "NOTE: Each of the multiple stems should be included in these files. You may indicate in the codes\n" + - "field which one is the main stem (if the tree has only one stem, you do not have to include the main\n" + - "stem code). The rest of the information should be repeated for each multiple stem. Make sure that\n" + - "the information (species code, date, etc.) is exactly the same for all multiple stems of the same tree. "; + 'NOTE: Each of the multiple stems should be included in these files. You may indicate in the codes\n' + + 'field which one is the main stem (if the tree has only one stem, you do not have to include the main\n' + + 'stem code). The rest of the information should be repeated for each multiple stem. Make sure that\n' + + 'the information (species code, date, etc.) is exactly the same for all multiple stems of the same tree. '; const _censusNote2 = - "NOTE: The dataset for each census should only contain trees and stems that were tagged and\n" + - "measured from that census. The dataset for subsequent censuses should contain all live stems from\n" + - "the previous census. Dead or lost stems should have the appropriate codes to indicate their absence\n" + - "in subsequent censuses."; + 'NOTE: The dataset for each census should only contain trees and stems that were tagged and\n' + + 'measured from that census. The dataset for subsequent censuses should contain all live stems from\n' + + 'the previous census. Dead or lost stems should have the appropriate codes to indicate their absence\n' + + 'in subsequent censuses.'; return ( - }> - + }> + Core Functions and Features @@ -104,16 +104,16 @@ export default function DashboardPage() { - + } onClick={triggerPulse}> - + This is a feedback form! - + Completing a Census Description of the card. diff --git a/frontend/app/(hub)/error.tsx b/frontend/app/(hub)/error.tsx index 7239e2bd..b5dba8c2 100644 --- a/frontend/app/(hub)/error.tsx +++ b/frontend/app/(hub)/error.tsx @@ -1,7 +1,7 @@ -"use client"; +'use client'; -import React, { useEffect } from "react"; -import { Box, Typography, Button } from "@mui/joy"; +import React, { useEffect } from 'react'; +import { Box, Typography, Button } from '@mui/joy'; const ErrorPage = ({ error, reset }: { error: Error; reset: () => void }) => { useEffect(() => { @@ -12,7 +12,7 @@ const ErrorPage = ({ error, reset }: { error: Error; reset: () => void }) => { }, [reset]); return ( - + Something went wrong - Core Hub Layout Page {error.message} Retrying in 5 seconds... diff --git a/frontend/app/(hub)/fixeddatainput/alltaxonomies/error.tsx b/frontend/app/(hub)/fixeddatainput/alltaxonomies/error.tsx index f1b705ed..655b021b 100644 --- a/frontend/app/(hub)/fixeddatainput/alltaxonomies/error.tsx +++ b/frontend/app/(hub)/fixeddatainput/alltaxonomies/error.tsx @@ -1,7 +1,7 @@ -"use client"; +'use client'; -import React, { useEffect } from "react"; -import { Box, Typography, Button } from "@mui/joy"; +import React, { useEffect } from 'react'; +import { Box, Typography, Button } from '@mui/joy'; const ErrorPage = ({ error, reset }: { error: Error; reset: () => void }) => { useEffect(() => { @@ -12,7 +12,7 @@ const ErrorPage = ({ error, reset }: { error: Error; reset: () => void }) => { }, [reset]); return ( - + Something went wrong - Species List Page {error.message} Retrying in 5 seconds... diff --git a/frontend/app/(hub)/fixeddatainput/alltaxonomies/page.tsx b/frontend/app/(hub)/fixeddatainput/alltaxonomies/page.tsx index f596e379..f3655f3f 100644 --- a/frontend/app/(hub)/fixeddatainput/alltaxonomies/page.tsx +++ b/frontend/app/(hub)/fixeddatainput/alltaxonomies/page.tsx @@ -1,4 +1,4 @@ -import AllTaxonomiesViewDataGrid from "@/components/datagrids/applications/alltaxonomiesviewdatagrid"; +import AllTaxonomiesViewDataGrid from '@/components/datagrids/applications/alltaxonomiesviewdatagrid'; export default function AllTaxonomiesPage() { return ; diff --git a/frontend/app/(hub)/fixeddatainput/attributes/error.tsx b/frontend/app/(hub)/fixeddatainput/attributes/error.tsx index 87b8911d..1bdaf2b6 100644 --- a/frontend/app/(hub)/fixeddatainput/attributes/error.tsx +++ b/frontend/app/(hub)/fixeddatainput/attributes/error.tsx @@ -1,7 +1,7 @@ -"use client"; +'use client'; -import React, { useEffect } from "react"; -import { Box, Typography, Button } from "@mui/joy"; +import React, { useEffect } from 'react'; +import { Box, Typography, Button } from '@mui/joy'; const ErrorPage = ({ error, reset }: { error: Error; reset: () => void }) => { useEffect(() => { @@ -12,7 +12,7 @@ const ErrorPage = ({ error, reset }: { error: Error; reset: () => void }) => { }, [reset]); return ( - + Something went wrong - Stem Codes Page {error.message} Retrying in 5 seconds... diff --git a/frontend/app/(hub)/fixeddatainput/attributes/page.tsx b/frontend/app/(hub)/fixeddatainput/attributes/page.tsx index d5c5304c..e4987af1 100644 --- a/frontend/app/(hub)/fixeddatainput/attributes/page.tsx +++ b/frontend/app/(hub)/fixeddatainput/attributes/page.tsx @@ -1,4 +1,4 @@ -import AttributesDataGrid from "@/components/datagrids/applications/attributesdatagrid"; +import AttributesDataGrid from '@/components/datagrids/applications/attributesdatagrid'; export default function AttributesPage() { return ; diff --git a/frontend/app/(hub)/fixeddatainput/census/error.tsx b/frontend/app/(hub)/fixeddatainput/census/error.tsx index f146d524..553194c9 100644 --- a/frontend/app/(hub)/fixeddatainput/census/error.tsx +++ b/frontend/app/(hub)/fixeddatainput/census/error.tsx @@ -1,7 +1,7 @@ -"use client"; +'use client'; -import React, { useEffect } from "react"; -import { Box, Typography, Button } from "@mui/joy"; +import React, { useEffect } from 'react'; +import { Box, Typography, Button } from '@mui/joy'; const ErrorPage = ({ error, reset }: { error: Error; reset: () => void }) => { useEffect(() => { @@ -12,7 +12,7 @@ const ErrorPage = ({ error, reset }: { error: Error; reset: () => void }) => { }, [reset]); return ( - + Something went wrong - Census Page {error.message} Retrying in 5 seconds... diff --git a/frontend/app/(hub)/fixeddatainput/census/page.tsx b/frontend/app/(hub)/fixeddatainput/census/page.tsx index 59e66a7b..62bcd2d6 100644 --- a/frontend/app/(hub)/fixeddatainput/census/page.tsx +++ b/frontend/app/(hub)/fixeddatainput/census/page.tsx @@ -1,4 +1,4 @@ -import CensusDataGrid from "@/components/datagrids/applications/censusdatagrid"; +import CensusDataGrid from '@/components/datagrids/applications/censusdatagrid'; export default function CensusPage() { return ; diff --git a/frontend/app/(hub)/fixeddatainput/personnel/error.tsx b/frontend/app/(hub)/fixeddatainput/personnel/error.tsx index c02d8bf8..752c5d8f 100644 --- a/frontend/app/(hub)/fixeddatainput/personnel/error.tsx +++ b/frontend/app/(hub)/fixeddatainput/personnel/error.tsx @@ -1,7 +1,7 @@ -"use client"; +'use client'; -import React, { useEffect } from "react"; -import { Box, Typography, Button } from "@mui/joy"; +import React, { useEffect } from 'react'; +import { Box, Typography, Button } from '@mui/joy'; const ErrorPage = ({ error, reset }: { error: Error; reset: () => void }) => { useEffect(() => { @@ -12,7 +12,7 @@ const ErrorPage = ({ error, reset }: { error: Error; reset: () => void }) => { }, [reset]); return ( - + Something went wrong - Personnel Page {error.message} Retrying in 5 seconds... diff --git a/frontend/app/(hub)/fixeddatainput/personnel/page.tsx b/frontend/app/(hub)/fixeddatainput/personnel/page.tsx index b848e1e0..e1e66012 100644 --- a/frontend/app/(hub)/fixeddatainput/personnel/page.tsx +++ b/frontend/app/(hub)/fixeddatainput/personnel/page.tsx @@ -1,4 +1,4 @@ -import PersonnelDataGrid from "@/components/datagrids/applications/personneldatagrid"; +import PersonnelDataGrid from '@/components/datagrids/applications/personneldatagrid'; export default function PersonnelPage() { return ; diff --git a/frontend/app/(hub)/fixeddatainput/quadratpersonnel/error.tsx b/frontend/app/(hub)/fixeddatainput/quadratpersonnel/error.tsx index a04a5adb..652340d4 100644 --- a/frontend/app/(hub)/fixeddatainput/quadratpersonnel/error.tsx +++ b/frontend/app/(hub)/fixeddatainput/quadratpersonnel/error.tsx @@ -1,7 +1,7 @@ -"use client"; +'use client'; -import React, { useEffect } from "react"; -import { Box, Typography, Button } from "@mui/joy"; +import React, { useEffect } from 'react'; +import { Box, Typography, Button } from '@mui/joy'; const ErrorPage = ({ error, reset }: { error: Error; reset: () => void }) => { useEffect(() => { @@ -12,7 +12,7 @@ const ErrorPage = ({ error, reset }: { error: Error; reset: () => void }) => { }, [reset]); return ( - + Something went wrong - QuadratPersonnel Page {error.message} Retrying in 5 seconds... diff --git a/frontend/app/(hub)/fixeddatainput/quadratpersonnel/page.tsx b/frontend/app/(hub)/fixeddatainput/quadratpersonnel/page.tsx index b6c77426..258c96e7 100644 --- a/frontend/app/(hub)/fixeddatainput/quadratpersonnel/page.tsx +++ b/frontend/app/(hub)/fixeddatainput/quadratpersonnel/page.tsx @@ -1,4 +1,4 @@ -import QuadratPersonnelDataGrid from "@/components/datagrids/applications/quadratpersonneldatagrid"; +import QuadratPersonnelDataGrid from '@/components/datagrids/applications/quadratpersonneldatagrid'; export default function QuadratPersonnelPage() { return ; diff --git a/frontend/app/(hub)/fixeddatainput/quadrats/error.tsx b/frontend/app/(hub)/fixeddatainput/quadrats/error.tsx index 7f3236ad..f7b106ef 100644 --- a/frontend/app/(hub)/fixeddatainput/quadrats/error.tsx +++ b/frontend/app/(hub)/fixeddatainput/quadrats/error.tsx @@ -1,7 +1,7 @@ -"use client"; +'use client'; -import React, { useEffect } from "react"; -import { Box, Typography, Button } from "@mui/joy"; +import React, { useEffect } from 'react'; +import { Box, Typography, Button } from '@mui/joy'; const ErrorPage = ({ error, reset }: { error: Error; reset: () => void }) => { useEffect(() => { @@ -12,7 +12,7 @@ const ErrorPage = ({ error, reset }: { error: Error; reset: () => void }) => { }, [reset]); return ( - + Something went wrong - Quadrats Page {error.message} Retrying in 5 seconds... diff --git a/frontend/app/(hub)/fixeddatainput/quadrats/page.tsx b/frontend/app/(hub)/fixeddatainput/quadrats/page.tsx index 2fdb6afa..7af8a2f4 100644 --- a/frontend/app/(hub)/fixeddatainput/quadrats/page.tsx +++ b/frontend/app/(hub)/fixeddatainput/quadrats/page.tsx @@ -1,4 +1,4 @@ -import QuadratsDataGrid from "@/components/datagrids/applications/quadratsdatagrid"; +import QuadratsDataGrid from '@/components/datagrids/applications/quadratsdatagrid'; export default function QuadratsPage() { return ; diff --git a/frontend/app/(hub)/fixeddatainput/species/error.tsx b/frontend/app/(hub)/fixeddatainput/species/error.tsx index 34a46caf..e7d071c6 100644 --- a/frontend/app/(hub)/fixeddatainput/species/error.tsx +++ b/frontend/app/(hub)/fixeddatainput/species/error.tsx @@ -1,7 +1,7 @@ -"use client"; +'use client'; -import React, { useEffect } from "react"; -import { Box, Typography, Button } from "@mui/joy"; +import React, { useEffect } from 'react'; +import { Box, Typography, Button } from '@mui/joy'; const ErrorPage = ({ error, reset }: { error: Error; reset: () => void }) => { useEffect(() => { @@ -12,7 +12,7 @@ const ErrorPage = ({ error, reset }: { error: Error; reset: () => void }) => { }, [reset]); return ( - + Something went wrong - Species Page {error.message} Retrying in 5 seconds... diff --git a/frontend/app/(hub)/fixeddatainput/species/page.tsx b/frontend/app/(hub)/fixeddatainput/species/page.tsx index e9f04436..3b53a17d 100644 --- a/frontend/app/(hub)/fixeddatainput/species/page.tsx +++ b/frontend/app/(hub)/fixeddatainput/species/page.tsx @@ -1,4 +1,4 @@ -import SpeciesDataGrid from "@/components/datagrids/applications/speciesdatagrid"; +import SpeciesDataGrid from '@/components/datagrids/applications/speciesdatagrid'; export default function SpeciesPage() { return ; diff --git a/frontend/app/(hub)/fixeddatainput/stemtaxonomies/error.tsx b/frontend/app/(hub)/fixeddatainput/stemtaxonomies/error.tsx index fff2fa8a..100cea27 100644 --- a/frontend/app/(hub)/fixeddatainput/stemtaxonomies/error.tsx +++ b/frontend/app/(hub)/fixeddatainput/stemtaxonomies/error.tsx @@ -1,7 +1,7 @@ -"use client"; +'use client'; -import React, { useEffect } from "react"; -import { Box, Typography, Button } from "@mui/joy"; +import React, { useEffect } from 'react'; +import { Box, Typography, Button } from '@mui/joy'; const ErrorPage = ({ error, reset }: { error: Error; reset: () => void }) => { useEffect(() => { @@ -12,7 +12,7 @@ const ErrorPage = ({ error, reset }: { error: Error; reset: () => void }) => { }, [reset]); return ( - + Something went wrong - Plot-Species List {error.message} Retrying in 5 seconds... diff --git a/frontend/app/(hub)/fixeddatainput/stemtaxonomies/page.tsx b/frontend/app/(hub)/fixeddatainput/stemtaxonomies/page.tsx index 6adec4ea..b583ff35 100644 --- a/frontend/app/(hub)/fixeddatainput/stemtaxonomies/page.tsx +++ b/frontend/app/(hub)/fixeddatainput/stemtaxonomies/page.tsx @@ -1,6 +1,6 @@ -"use client"; +'use client'; -import StemTaxonomiesViewDataGrid from "@/components/datagrids/applications/stemtaxonomiesviewdatagrid"; +import StemTaxonomiesViewDataGrid from '@/components/datagrids/applications/stemtaxonomiesviewdatagrid'; export default function StemTaxonomiesPage() { return ; diff --git a/frontend/app/(hub)/fixeddatainput/subquadrats/error.tsx b/frontend/app/(hub)/fixeddatainput/subquadrats/error.tsx index 7ce40ccf..b11fe926 100644 --- a/frontend/app/(hub)/fixeddatainput/subquadrats/error.tsx +++ b/frontend/app/(hub)/fixeddatainput/subquadrats/error.tsx @@ -1,7 +1,7 @@ -"use client"; +'use client'; -import React, { useEffect } from "react"; -import { Box, Typography, Button } from "@mui/joy"; +import React, { useEffect } from 'react'; +import { Box, Typography, Button } from '@mui/joy'; const ErrorPage = ({ error, reset }: { error: Error; reset: () => void }) => { useEffect(() => { @@ -12,7 +12,7 @@ const ErrorPage = ({ error, reset }: { error: Error; reset: () => void }) => { }, [reset]); return ( - + Something went wrong - Subquadrats Page {error.message} Retrying in 5 seconds... diff --git a/frontend/app/(hub)/fixeddatainput/subquadrats/page.tsx b/frontend/app/(hub)/fixeddatainput/subquadrats/page.tsx index 92ab8e73..f7a082f1 100644 --- a/frontend/app/(hub)/fixeddatainput/subquadrats/page.tsx +++ b/frontend/app/(hub)/fixeddatainput/subquadrats/page.tsx @@ -1,4 +1,4 @@ -import SubquadratsDataGrid from "@/components/datagrids/applications/sqdatagrid"; +import SubquadratsDataGrid from '@/components/datagrids/applications/sqdatagrid'; export default function SubquadratsPage() { return ; diff --git a/frontend/app/(hub)/layout.tsx b/frontend/app/(hub)/layout.tsx index a2d7fe8b..3df50d7b 100644 --- a/frontend/app/(hub)/layout.tsx +++ b/frontend/app/(hub)/layout.tsx @@ -1,14 +1,14 @@ -"use client"; -import React, { useEffect, useCallback, useRef, useState } from "react"; -import { title } from "@/config/primitives"; -import { useSession } from "next-auth/react"; -import { redirect, usePathname } from "next/navigation"; -import dynamic from "next/dynamic"; -import { Box, IconButton, Stack, Tooltip, Typography } from "@mui/joy"; -import Divider from "@mui/joy/Divider"; -import { useLoading } from "@/app/contexts/loadingprovider"; -import { getAllSchemas } from "@/components/processors/processorhelperfunctions"; -import { useSiteContext, usePlotContext, useOrgCensusContext, useQuadratContext } from "@/app/contexts/userselectionprovider"; +'use client'; +import React, { useEffect, useCallback, useRef, useState } from 'react'; +import { title } from '@/config/primitives'; +import { useSession } from 'next-auth/react'; +import { redirect, usePathname } from 'next/navigation'; +import dynamic from 'next/dynamic'; +import { Box, IconButton, Stack, Tooltip, Typography } from '@mui/joy'; +import Divider from '@mui/joy/Divider'; +import { useLoading } from '@/app/contexts/loadingprovider'; +import { getAllSchemas } from '@/components/processors/processorhelperfunctions'; +import { useSiteContext, usePlotContext, useOrgCensusContext, useQuadratContext } from '@/app/contexts/userselectionprovider'; import { useOrgCensusListContext, useOrgCensusListDispatch, @@ -19,67 +19,67 @@ import { useSiteListDispatch, useSubquadratListContext, useSubquadratListDispatch -} from "@/app/contexts/listselectionprovider"; -import { createAndUpdateCensusList } from "@/config/sqlrdsdefinitions/orgcensusrds"; -import { siteConfig } from "@/config/macros/siteconfigs"; -import { AcaciaVersionTypography } from "@/styles/versions/acaciaversion"; -import GithubFeedbackModal from "@/components/client/githubfeedbackmodal"; -import HelpOutlineOutlinedIcon from "@mui/icons-material/HelpOutlineOutlined"; +} from '@/app/contexts/listselectionprovider'; +import { createAndUpdateCensusList } from '@/config/sqlrdsdefinitions/orgcensusrds'; +import { siteConfig } from '@/config/macros/siteconfigs'; +import { AcaciaVersionTypography } from '@/styles/versions/acaciaversion'; +import GithubFeedbackModal from '@/components/client/githubfeedbackmodal'; +import HelpOutlineOutlinedIcon from '@mui/icons-material/HelpOutlineOutlined'; -import { useDataValidityContext } from "../contexts/datavalidityprovider"; -import { useLockAnimation } from "../contexts/lockanimationcontext"; +import { useDataValidityContext } from '../contexts/datavalidityprovider'; +import { useLockAnimation } from '../contexts/lockanimationcontext'; -const Sidebar = dynamic(() => import("@/components/sidebar"), { ssr: false }); -const Header = dynamic(() => import("@/components/header"), { ssr: false }); +const Sidebar = dynamic(() => import('@/components/sidebar'), { ssr: false }); +const Header = dynamic(() => import('@/components/header'), { ssr: false }); function renderSwitch(endpoint: string) { const commonStyle = { - display: "flex", + display: 'flex', flex: 1, - alignItems: "center", - justifyContent: "center", - textAlign: "center", - minHeight: "50px" + alignItems: 'center', + justifyContent: 'center', + textAlign: 'center', + minHeight: '50px' }; - let output: string = ""; + let output: string = ''; switch (endpoint) { - case "/dashboard": - output = "Dashboard"; + case '/dashboard': + output = 'Dashboard'; break; - case "/measurementshub/summary": - output = "View Data"; + case '/measurementshub/summary': + output = 'View Data'; break; - case "/measurementshub/validationhistory": - output = "Validation History"; + case '/measurementshub/validationhistory': + output = 'Validation History'; break; - case "/fixeddatainput/attributes": - output = "Stem Codes"; + case '/fixeddatainput/attributes': + output = 'Stem Codes'; break; - case "/fixeddatainput/personnel": - output = "Personnel"; + case '/fixeddatainput/personnel': + output = 'Personnel'; break; - case "/fixeddatainput/quadrats": - output = "Quadrats"; + case '/fixeddatainput/quadrats': + output = 'Quadrats'; break; - case "/fixeddatainput/subquadrats": - output = "Subquadrats"; + case '/fixeddatainput/subquadrats': + output = 'Subquadrats'; break; - case "/fixeddatainput/stemtaxonomies": - output = "Plot-Species List"; + case '/fixeddatainput/stemtaxonomies': + output = 'Plot-Species List'; break; - case "/fixeddatainput/quadratpersonnel": - output = "Quadrat-Assigned Personnel"; + case '/fixeddatainput/quadratpersonnel': + output = 'Quadrat-Assigned Personnel'; break; - case "/fixeddatainput/alltaxonomies": - output = "Species List"; + case '/fixeddatainput/alltaxonomies': + output = 'Species List'; break; } return ( -

+

{output}

@@ -124,16 +124,16 @@ export default function HubLayout({ children }: { children: React.ReactNode }) { if (!currentPlot) return { success: false, - message: "Plot must be selected to load census data" + message: 'Plot must be selected to load census data' }; if (censusListContext !== undefined && censusListContext.length > 0) return { success: true }; - setLoading(true, "Loading raw census data"); - const response = await fetch(`/api/fetchall/census/${currentPlot.plotID}?schema=${currentSite?.schemaName || ""}`); + setLoading(true, 'Loading raw census data'); + const response = await fetch(`/api/fetchall/census/${currentPlot.plotID}?schema=${currentSite?.schemaName || ''}`); const censusRDSLoad = await response.json(); setLoading(false); - setLoading(true, "Converting raw census data..."); + setLoading(true, 'Converting raw census data...'); const censusList = await createAndUpdateCensusList(censusRDSLoad); if (censusListDispatch) { await censusListDispatch({ censusList }); @@ -147,20 +147,20 @@ export default function HubLayout({ children }: { children: React.ReactNode }) { if (!currentSite) return { success: false, - message: "Site must be selected to load plot data" + message: 'Site must be selected to load plot data' }; if (plotListContext !== undefined && plotListContext.length > 0) return { success: true }; - setLoading(true, "Loading plot list information..."); - const plotsResponse = await fetch(`/api/fetchall/plots?schema=${currentSite?.schemaName || ""}`); + setLoading(true, 'Loading plot list information...'); + const plotsResponse = await fetch(`/api/fetchall/plots?schema=${currentSite?.schemaName || ''}`); const plotsData = await plotsResponse.json(); - if (!plotsData) return { success: false, message: "Failed to load plots data" }; + if (!plotsData) return { success: false, message: 'Failed to load plots data' }; setLoading(false); - setLoading(true, "Dispatching plot list information..."); + setLoading(true, 'Dispatching plot list information...'); if (plotListDispatch) { await plotListDispatch({ plotList: plotsData }); - } else return { success: false, message: "Failed to dispatch plots data" }; + } else return { success: false, message: 'Failed to dispatch plots data' }; setLoading(false); setPlotListLoaded(true); return { success: true }; @@ -170,22 +170,22 @@ export default function HubLayout({ children }: { children: React.ReactNode }) { if (!currentPlot || !currentCensus) return { success: false, - message: "Plot and Census must be selected to load quadrat data" + message: 'Plot and Census must be selected to load quadrat data' }; if (quadratListContext !== undefined && quadratListContext.length > 0) return { success: true }; - setLoading(true, "Loading quadrat list information..."); + setLoading(true, 'Loading quadrat list information...'); const quadratsResponse = await fetch( - `/api/fetchall/quadrats/${currentPlot.plotID}/${currentCensus.plotCensusNumber}?schema=${currentSite?.schemaName || ""}` + `/api/fetchall/quadrats/${currentPlot.plotID}/${currentCensus.plotCensusNumber}?schema=${currentSite?.schemaName || ''}` ); const quadratsData = await quadratsResponse.json(); - if (!quadratsData) return { success: false, message: "Failed to load quadrats data" }; + if (!quadratsData) return { success: false, message: 'Failed to load quadrats data' }; setLoading(false); - setLoading(true, "Dispatching quadrat list information..."); + setLoading(true, 'Dispatching quadrat list information...'); if (quadratListDispatch) { await quadratListDispatch({ quadratList: quadratsData }); - } else return { success: false, message: "Failed to dispatch quadrats data" }; + } else return { success: false, message: 'Failed to dispatch quadrats data' }; setLoading(false); setQuadratListLoaded(true); return { success: true }; @@ -195,34 +195,34 @@ export default function HubLayout({ children }: { children: React.ReactNode }) { if (!currentPlot || !currentCensus || !currentQuadrat) return { success: false, - message: "Plot, Census, and Quadrat must be selected to load subquadrat data" + message: 'Plot, Census, and Quadrat must be selected to load subquadrat data' }; if (subquadratListContext !== undefined && subquadratListContext.length > 0) return { success: true }; - setLoading(true, "Loading subquadrat list information..."); + setLoading(true, 'Loading subquadrat list information...'); const subquadratResponse = await fetch( - `/api/fetchall/subquadrats/${currentPlot.plotID}/${currentCensus.plotCensusNumber}/${currentQuadrat.quadratID}?schema=${currentSite?.schemaName || ""}` + `/api/fetchall/subquadrats/${currentPlot.plotID}/${currentCensus.plotCensusNumber}/${currentQuadrat.quadratID}?schema=${currentSite?.schemaName || ''}` ); const subquadratData = await subquadratResponse.json(); - if (!subquadratData) return { success: false, message: "Failed to load subquadrats data" }; + if (!subquadratData) return { success: false, message: 'Failed to load subquadrats data' }; setLoading(false); - setLoading(true, "Dispatching subquadrat list information..."); + setLoading(true, 'Dispatching subquadrat list information...'); if (subquadratListDispatch) { await subquadratListDispatch({ subquadratList: subquadratData }); - } else return { success: false, message: "Failed to dispatch subquadrat list" }; + } else return { success: false, message: 'Failed to dispatch subquadrat list' }; setLoading(false); setSubquadratListLoaded(true); return { success: true }; }, [subquadratListContext, subquadratListDispatch, currentPlot, currentCensus, currentQuadrat, currentSite, setLoading, validity]); const fetchSiteList = useCallback(async () => { - setLoading(true, "Loading Sites..."); + setLoading(true, 'Loading Sites...'); try { if (session && !siteListLoaded) { const sites = session?.user?.allsites ?? []; if (sites.length === 0) { - throw new Error("Session sites undefined"); + throw new Error('Session sites undefined'); } else { siteListDispatch ? await siteListDispatch({ siteList: sites }) : undefined; } @@ -293,7 +293,7 @@ export default function HubLayout({ children }: { children: React.ReactNode }) { useEffect(() => { if (manualReset) { - setLoading(true, "Manual refresh beginning..."); + setLoading(true, 'Manual refresh beginning...'); setPlotListLoaded(false); setCensusListLoaded(false); setQuadratListLoaded(false); @@ -306,7 +306,7 @@ export default function HubLayout({ children }: { children: React.ReactNode }) { useEffect(() => { // if contexts are reset due to website refresh, system needs to redirect user back to dashboard - if (currentSite === undefined && currentPlot === undefined && currentQuadrat === undefined && pathname !== "/dashboard") redirect("/dashboard"); + if (currentSite === undefined && currentPlot === undefined && currentQuadrat === undefined && pathname !== '/dashboard') redirect('/dashboard'); }, [pathname]); useEffect(() => { @@ -321,12 +321,12 @@ export default function HubLayout({ children }: { children: React.ReactNode }) { return ( <> @@ -337,81 +337,82 @@ export default function HubLayout({ children }: { children: React.ReactNode }) { component="main" className="MainContent" sx={{ - marginTop: "var(--Header-height)", - display: "flex", - flexDirection: "column", + marginTop: 'var(--Header-height)', + display: 'flex', + flexDirection: 'column', minWidth: 0, gap: 1, flexGrow: 1, flexShrink: 1, - overflow: "hidden", - minHeight: "calc(100vh - var(--Header-height) - 30px)", - marginLeft: isSidebarVisible ? "calc(var(--Sidebar-width) + 5px)" : "0", - transition: "margin-left 0.3s ease-in-out" + overflow: 'hidden', + minHeight: 'calc(100vh - var(--Header-height) - 30px)', + marginLeft: isSidebarVisible ? 'calc(var(--Sidebar-width) + 5px)' : '0', + transition: 'margin-left 0.3s ease-in-out' }} > {renderSwitch(pathname)} - + {children} - + } - className={isPulsing ? "animate-fade-blur-in" : ""} + className={isPulsing ? 'animate-fade-blur-in' : ''} > {siteConfig.name} - + - + {siteConfig.version} @@ -419,16 +420,16 @@ export default function HubLayout({ children }: { children: React.ReactNode }) { setIsFeedbackModalOpen(true)} - className={isPulsing ? "animate-pulse-no-opacity" : ""} + className={isPulsing ? 'animate-pulse-no-opacity' : ''} sx={{ - position: "fixed", + position: 'fixed', bottom: 20, right: 20, zIndex: 2000, - bgcolor: "primary.main", - color: "white", - "&:hover": { - bgcolor: "primary.dark" + bgcolor: 'primary.main', + color: 'white', + '&:hover': { + bgcolor: 'primary.dark' } }} > @@ -437,7 +438,6 @@ export default function HubLayout({ children }: { children: React.ReactNode }) { setIsFeedbackModalOpen(false)} /> - {/* setIsFeedbackModalOpen(false)} /> */} ); } diff --git a/frontend/app/(hub)/measurementshub/summary/error.tsx b/frontend/app/(hub)/measurementshub/summary/error.tsx index ef35b7d3..d14adf3c 100644 --- a/frontend/app/(hub)/measurementshub/summary/error.tsx +++ b/frontend/app/(hub)/measurementshub/summary/error.tsx @@ -1,7 +1,7 @@ -"use client"; +'use client'; -import React, { useEffect } from "react"; -import { Box, Typography, Button } from "@mui/joy"; +import React, { useEffect } from 'react'; +import { Box, Typography, Button } from '@mui/joy'; const ErrorPage = ({ error, reset }: { error: Error; reset: () => void }) => { useEffect(() => { @@ -12,7 +12,7 @@ const ErrorPage = ({ error, reset }: { error: Error; reset: () => void }) => { }, [reset]); return ( - + Something went wrong - View Data Page {error.message} Retrying in 5 seconds... diff --git a/frontend/app/(hub)/measurementshub/summary/page.tsx b/frontend/app/(hub)/measurementshub/summary/page.tsx index 9d552a70..3c7ef38e 100644 --- a/frontend/app/(hub)/measurementshub/summary/page.tsx +++ b/frontend/app/(hub)/measurementshub/summary/page.tsx @@ -1,20 +1,20 @@ -"use client"; -import React, { useEffect, useState } from "react"; -import { GridRowModes, GridRowModesModel, GridRowsProp } from "@mui/x-data-grid"; -import { Alert, AlertProps, Tooltip, TooltipProps, styled, tooltipClasses } from "@mui/material"; -import { initialMeasurementsSummaryViewRDSRow } from "@/config/sqlrdsdefinitions/views/measurementssummaryviewrds"; -import { Box, Typography, Button, Snackbar, Stack } from "@mui/joy"; -import Select, { SelectOption } from "@mui/joy/Select"; -import { useSession } from "next-auth/react"; -import { useOrgCensusContext, usePlotContext, useQuadratDispatch, useSiteContext } from "@/app/contexts/userselectionprovider"; -import { randomId } from "@mui/x-data-grid-generator"; -import UploadParentModal from "@/components/uploadsystemhelpers/uploadparentmodal"; -import { useQuadratListContext } from "@/app/contexts/listselectionprovider"; -import { Quadrat } from "@/config/sqlrdsdefinitions/tables/quadratrds"; -import Option from "@mui/joy/Option"; -import MeasurementSummaryGrid from "@/components/datagrids/msvdatagrid"; -import { useDataValidityContext } from "@/app/contexts/datavalidityprovider"; -import { msvGridColumns } from "@/components/client/datagridcolumns"; +'use client'; +import React, { useEffect, useState } from 'react'; +import { GridRowModes, GridRowModesModel, GridRowsProp } from '@mui/x-data-grid'; +import { Alert, AlertProps, Tooltip, TooltipProps, styled, tooltipClasses } from '@mui/material'; +import { initialMeasurementsSummaryViewRDSRow } from '@/config/sqlrdsdefinitions/views/measurementssummaryviewrds'; +import { Box, Typography, Button, Snackbar, Stack } from '@mui/joy'; +import Select, { SelectOption } from '@mui/joy/Select'; +import { useSession } from 'next-auth/react'; +import { useOrgCensusContext, usePlotContext, useQuadratDispatch, useSiteContext } from '@/app/contexts/userselectionprovider'; +import { randomId } from '@mui/x-data-grid-generator'; +import UploadParentModal from '@/components/uploadsystemhelpers/uploadparentmodal'; +import { useQuadratListContext } from '@/app/contexts/listselectionprovider'; +import { Quadrat } from '@/config/sqlrdsdefinitions/tables/quadratrds'; +import Option from '@mui/joy/Option'; +import MeasurementSummaryGrid from '@/components/datagrids/msvdatagrid'; +import { useDataValidityContext } from '@/app/contexts/datavalidityprovider'; +import { msvGridColumns } from '@/components/client/datagridcolumns'; const LargeTooltip = styled(({ className, ...props }: TooltipProps) => )(({ theme }) => ({ [`& .${tooltipClasses.tooltip}`]: { fontSize: 16, @@ -53,7 +53,7 @@ export default function SummaryPage() { const [rows, setRows] = React.useState([initialMeasurementsSummaryViewRDSRow] as GridRowsProp); const [rowCount, setRowCount] = useState(0); // total number of rows const [rowModesModel, setRowModesModel] = React.useState({}); - const [snackbar, setSnackbar] = React.useState | null>(null); + const [snackbar, setSnackbar] = React.useState | null>(null); const [refresh, setRefresh] = useState(false); const [paginationModel, setPaginationModel] = useState({ page: 0, @@ -116,7 +116,7 @@ export default function SummaryPage() { Select Quadrat: - - {!validity["quadrats"] && ( + {!validity['quadrats'] && ( No quadrats exist to be selected. @@ -165,34 +165,34 @@ export default function SummaryPage() { )} - + - + - + Note: This plot does not accept subquadrats.
Please ensure that you use quadrat names when submitting new measurements instead of subquadrat names
- {session?.user.userStatus !== "fieldcrew" ? ( + {session?.user.userStatus !== 'fieldcrew' ? ( {/* Note: ADMINISTRATOR VIEW @@ -215,18 +215,18 @@ export default function SummaryPage() { */} ) : ( - + If this setting is inaccurate, please contact an administrator. )}
- + -
@@ -334,7 +333,7 @@ ${pathname} )} {!createdIssue && loading && ( <> - Submitting issue... + Submitting issue... @@ -342,19 +341,19 @@ ${pathname} )} {createdIssue && ( <> - - - + + + Issue Created! - - } sx={{ justifyContent: "space-evenly", alignItems: "center" }}> + + } sx={{ justifyContent: 'space-evenly', alignItems: 'center' }}> - + {createdIssue.status} @@ -363,27 +362,27 @@ ${pathname} - + - {createdIssue.headers["location"].replace("api.github.com/repos", "github.com")} + {createdIssue.headers['location'].replace('api.github.com/repos', 'github.com')} - + @@ -392,16 +391,16 @@ ${pathname} {formatHeaders(createdIssue.headers)} - + Issue Details - - - Title:{" "} + + + Title:{' '} {createdIssue.data.title} @@ -469,13 +468,13 @@ ${pathname}
- + Created At: {new Date(createdIssue.data.created_at).toLocaleString()} - + Created By: {session?.user.name} diff --git a/frontend/components/client/loginfailure.tsx b/frontend/components/client/loginfailure.tsx index f85f25d5..7e9e47e0 100644 --- a/frontend/components/client/loginfailure.tsx +++ b/frontend/components/client/loginfailure.tsx @@ -1,26 +1,26 @@ -"use client"; +'use client'; -import { Button, Stack, Typography } from "@mui/joy"; -import { signOut } from "next-auth/react"; -import { useSearchParams } from "next/navigation"; +import { Button, Stack, Typography } from '@mui/joy'; +import { signOut } from 'next-auth/react'; +import { useSearchParams } from 'next/navigation'; const LoginFailed = () => { const searchParams = useSearchParams(); - let failureReason = searchParams.get("reason"); - if (failureReason === null || failureReason === "") failureReason = "Login failure triggered without reason. Please speak to an administrator"; + let failureReason = searchParams.get('reason'); + if (failureReason === null || failureReason === '') failureReason = 'Login failure triggered without reason. Please speak to an administrator'; const handleTryAgain = () => { sessionStorage.clear(); localStorage.clear(); - signOut({ callbackUrl: "/login" }).catch(console.error); + signOut({ callbackUrl: '/login' }).catch(console.error); }; return ( - - + + Oops! Login Failed - + Failure caused due to {failureReason} We couldn't log you in. Please try again or contact support for more help. diff --git a/frontend/components/client/rollovermodal.tsx b/frontend/components/client/rollovermodal.tsx index afb09aee..f4d316c8 100644 --- a/frontend/components/client/rollovermodal.tsx +++ b/frontend/components/client/rollovermodal.tsx @@ -1,18 +1,18 @@ -"use client"; -import * as React from "react"; -import { useEffect, useState } from "react"; -import CloseIcon from "@mui/icons-material/Close"; -import { QuadratRDS } from "@/config/sqlrdsdefinitions/tables/quadratrds"; -import { PersonnelRDS } from "@/config/sqlrdsdefinitions/tables/personnelrds"; -import { usePlotContext, useSiteContext } from "@/app/contexts/userselectionprovider"; -import { Stack } from "@mui/material"; -import { Button, Checkbox, IconButton, Modal, ModalDialog, Typography, Alert, DialogContent, DialogTitle, DialogActions, Select, Option, Grid } from "@mui/joy"; -import { DataGrid, GridRowSelectionModel } from "@mui/x-data-grid"; -import { useOrgCensusListContext } from "@/app/contexts/listselectionprovider"; -import { OrgCensusToCensusResultMapper } from "@/config/sqlrdsdefinitions/orgcensusrds"; -import { Box } from "@mui/system"; - -import { PersonnelGridColumns, quadratGridColumns } from "./datagridcolumns"; +'use client'; +import * as React from 'react'; +import { useEffect, useState } from 'react'; +import CloseIcon from '@mui/icons-material/Close'; +import { QuadratRDS } from '@/config/sqlrdsdefinitions/tables/quadratrds'; +import { PersonnelRDS } from '@/config/sqlrdsdefinitions/tables/personnelrds'; +import { usePlotContext, useSiteContext } from '@/app/contexts/userselectionprovider'; +import { Stack } from '@mui/material'; +import { Button, Checkbox, IconButton, Modal, ModalDialog, Typography, Alert, DialogContent, DialogTitle, DialogActions, Select, Option, Grid } from '@mui/joy'; +import { DataGrid, GridRowSelectionModel } from '@mui/x-data-grid'; +import { useOrgCensusListContext } from '@/app/contexts/listselectionprovider'; +import { OrgCensusToCensusResultMapper } from '@/config/sqlrdsdefinitions/orgcensusrds'; +import { Box } from '@mui/system'; + +import { PersonnelGridColumns, quadratGridColumns } from './datagridcolumns'; interface RolloverModalProps { open: boolean; @@ -64,8 +64,8 @@ export default function RolloverModal({ open, onClose, onConfirm }: RolloverModa setLoading(false); } catch (error) { - console.error("Failed to fetch previous data", error); - setError("Failed to fetch previous data. Please try again."); + console.error('Failed to fetch previous data', error); + setError('Failed to fetch previous data. Please try again.'); setLoading(false); } }; @@ -78,8 +78,8 @@ export default function RolloverModal({ open, onClose, onConfirm }: RolloverModa setPreviousPersonnel(personnelData); setLoading(false); } catch (error) { - console.error("Failed to fetch previous data", error); - setError("Failed to fetch previous data. Please try again."); + console.error('Failed to fetch previous data', error); + setError('Failed to fetch previous data. Please try again.'); setLoading(false); } }; @@ -172,28 +172,28 @@ export default function RolloverModal({ open, onClose, onConfirm }: RolloverModa const handleConfirm = async () => { if (confirmNoQuadratsRollover && confirmNoPersonnelRollover && selectedPersonnelCensus.censusID === 0 && selectedQuadratsCensus.censusID === 0) { - console.log("confirm no rollover"); + console.log('confirm no rollover'); onConfirm(rolloverPersonnel, rolloverQuadrats); resetState(); return; } else if (selectedPersonnelCensus.censusID === 0 && !confirmNoPersonnelRollover) { - alert("Please confirm that you do not wish to rollover personnel to proceed"); + alert('Please confirm that you do not wish to rollover personnel to proceed'); } else if (selectedQuadratsCensus.censusID === 0 && !confirmNoQuadratsRollover) { - alert("Please confirm that you do not wish to rollover quadrats to proceed"); + alert('Please confirm that you do not wish to rollover quadrats to proceed'); } if (!rolloverPersonnel && !rolloverQuadrats) { - alert("You must select at least one option to roll over or confirm no rollover."); + alert('You must select at least one option to roll over or confirm no rollover.'); return; } else if (rolloverQuadrats && selectedQuadrats.length === 0 && customizeQuadrats) { - alert("You must select at least one quadrat to roll over."); + alert('You must select at least one quadrat to roll over.'); return; } else if (rolloverPersonnel && selectedPersonnel.length === 0 && customizePersonnel) { - alert("You must select at least one person to roll over."); + alert('You must select at least one person to roll over.'); return; } - if (!currentSite?.schemaName || !currentPlot?.plotID) throw new Error("site context and plot context are undefined."); + if (!currentSite?.schemaName || !currentPlot?.plotID) throw new Error('site context and plot context are undefined.'); setLoading(true); @@ -206,19 +206,19 @@ export default function RolloverModal({ open, onClose, onConfirm }: RolloverModa censusListContext[0]?.plotCensusNumber ?? 0 ) : 0; - if (!highestPlotCensusNumber) throw new Error("highest plot census number calculation failed"); + if (!highestPlotCensusNumber) throw new Error('highest plot census number calculation failed'); const mapper = new OrgCensusToCensusResultMapper(); const newCensusID = await mapper.startNewCensus(currentSite?.schemaName, currentPlot?.plotID, highestPlotCensusNumber + 1); - if (!newCensusID) throw new Error("census creation failure"); + if (!newCensusID) throw new Error('census creation failure'); // Perform the rollover if (rolloverPersonnel) { // passing source censusID to rollover endpoint - console.log("rollover personnel"); + console.log('rollover personnel'); await fetch(`/api/rollover/personnel/${currentSite?.schemaName}/${currentPlot?.plotID}/${selectedPersonnelCensus?.censusID}/${newCensusID}`, { - method: "POST", + method: 'POST', headers: { - "Content-Type": "application/json" + 'Content-Type': 'application/json' }, body: JSON.stringify({ incoming: customizePersonnel ? selectedPersonnel.map(person => person.personnelID) : previousPersonnel.map(person => person.personnelID) @@ -229,9 +229,9 @@ export default function RolloverModal({ open, onClose, onConfirm }: RolloverModa if (rolloverQuadrats) { // passing source censusID to rollover endpoint await fetch(`/api/rollover/quadrats/${currentSite?.schemaName}/${currentPlot?.plotID}/${selectedQuadratsCensus?.censusID}/${newCensusID}`, { - method: "POST", + method: 'POST', headers: { - "Content-Type": "application/json" + 'Content-Type': 'application/json' }, body: JSON.stringify({ incoming: customizeQuadrats ? selectedQuadrats.map(quadrat => quadrat.quadratID) : previousQuadrats.map(quadrat => quadrat.quadratID) @@ -241,7 +241,7 @@ export default function RolloverModal({ open, onClose, onConfirm }: RolloverModa onConfirm(rolloverPersonnel, rolloverQuadrats, newCensusID); } else onConfirm(false, false); } catch (error) { - console.error("Failed to perform rollover", error); + console.error('Failed to perform rollover', error); setError(`Failed to perform rollover: ${error}. Please try again.`); onConfirm(false, false); } finally { @@ -278,33 +278,33 @@ export default function RolloverModal({ open, onClose, onConfirm }: RolloverModa Rollover Census Data - + {error && {error}} - - - + + + Roll over Personnel data: {selectedPersonnelCensus.censusID === 0 ? ( - + You have selected to not roll over any Personnel data.
Please confirm to proceed.
) : ( - + Roll over personnel data
{rolloverPersonnel && ( - + {customizePersonnel && ( )} - - + + Roll over Quadrats data {selectedQuadratsCensus.censusID === 0 ? ( - + You have selected to not roll over any Quadrats data.
Please confirm to proceed.
) : ( - + Roll over quadrats data
{rolloverQuadrats && ( - + {customizeQuadrats && ( { // need to re-fetch the census list --> can't use list context here because it's outdated! - const response = await fetch(`/api/fetchall/census/${currentPlot?.plotID}?schema=${currentSite?.schemaName || ""}`); + const response = await fetch(`/api/fetchall/census/${currentPlot?.plotID}?schema=${currentSite?.schemaName || ''}`); const censusRDSLoad = await response.json(); const censusList = await createAndUpdateCensusList(censusRDSLoad); setUpdatedCensusList(censusList); @@ -127,23 +127,23 @@ export default function RolloverStemsModal({ open, onClose, onConfirm }: Rollove const handleConfirm = async () => { if (confirmNoStemsRollover && selectedStemsCensus.censusID === 0) { - console.log("confirm no rollover (stems)"); + console.log('confirm no rollover (stems)'); onConfirm(false); resetState(); return; } else if (selectedStemsCensus.censusID === 0 && !confirmNoStemsRollover) { - alert("Please confirm that you do not wish to rollover stems to proceed"); + alert('Please confirm that you do not wish to rollover stems to proceed'); } if (!rolloverStems) { - alert("You must select at least one option to roll over or confirm no rollover."); + alert('You must select at least one option to roll over or confirm no rollover.'); return; } else if (rolloverStems && selectedStems.length === 0 && customizeStems) { - alert("You must select at least one stem to roll over"); + alert('You must select at least one stem to roll over'); return; } - if (!currentSite?.schemaName || !currentPlot?.plotID) throw new Error("site context OR plot context are undefined"); + if (!currentSite?.schemaName || !currentPlot?.plotID) throw new Error('site context OR plot context are undefined'); setLoading(true); try { @@ -155,24 +155,24 @@ export default function RolloverStemsModal({ open, onClose, onConfirm }: Rollove updatedCensusList[0]?.plotCensusNumber ?? 0 ) : 0; - if (!highestPlotCensusNumber) throw new Error("highest plot census number calculation failed"); + if (!highestPlotCensusNumber) throw new Error('highest plot census number calculation failed'); const mapper = new OrgCensusToCensusResultMapper(); const newCensusID = await mapper.startNewCensus(currentSite?.schemaName, currentPlot?.plotID, highestPlotCensusNumber + 1); - if (!newCensusID) throw new Error("census creation failure"); + if (!newCensusID) throw new Error('census creation failure'); - console.log("rollover personnel"); + console.log('rollover personnel'); await fetch(`/api/rollover/personnel/${currentSite?.schemaName}/${currentPlot?.plotID}/${selectedStemsCensus?.censusID}/${newCensusID}`, { - method: "POST", + method: 'POST', headers: { - "Content-Type": "application/json" + 'Content-Type': 'application/json' }, body: JSON.stringify({ incoming: customizeStems ? selectedStems.map(stem => stem.stemID) : previousStems.map(stem => stem.stemID) }) }); } onConfirm(rolloverStems); } catch (error) { - console.error("failed to perform stems rollover: ", error); + console.error('failed to perform stems rollover: ', error); onConfirm(false); } finally { setLoading(false); @@ -204,16 +204,16 @@ export default function RolloverStemsModal({ open, onClose, onConfirm }: Rollove - + Rollover Stems Data - + - - + + Roll over Stems data: { // Find the corresponding Quadrat object using newValue @@ -171,14 +171,14 @@ export default function UploadStart(props: Readonly) { setQuadrat(selectedQuadrat); }} > - + {quadratList?.map(item => ( )} - {uploadForm === "measurements" && personnelRecording !== "" && currentQuadrat && !finish && ( + {uploadForm === 'measurements' && personnelRecording !== '' && currentQuadrat && !finish && ( <> You have selected: Form: {uploadForm} @@ -199,7 +199,7 @@ export default function UploadStart(props: Readonly) { Personnel: {personnelRecording} )} - {["attributes", "personnel", "species", "quadrats", "subquadrats"].includes(uploadForm) && personnelRecording !== "" && currentQuadrat && !finish && ( + {['attributes', 'personnel', 'species', 'quadrats', 'subquadrats'].includes(uploadForm) && personnelRecording !== '' && currentQuadrat && !finish && ( <> You have selected: Form: {uploadForm} diff --git a/frontend/components/uploadsystem/segments/uploadupdatevalidations.tsx b/frontend/components/uploadsystem/segments/uploadupdatevalidations.tsx index 8877aa19..91c250ee 100644 --- a/frontend/components/uploadsystem/segments/uploadupdatevalidations.tsx +++ b/frontend/components/uploadsystem/segments/uploadupdatevalidations.tsx @@ -1,16 +1,16 @@ -"use client"; -import React, { useEffect, useState } from "react"; -import { Box, Typography } from "@mui/material"; -import { ReviewStates } from "@/config/macros/uploadsystemmacros"; -import { UploadUpdateValidationsProps } from "@/config/macros/uploadsystemmacros"; -import { useOrgCensusContext, usePlotContext } from "@/app/contexts/userselectionprovider"; +'use client'; +import React, { useEffect, useState } from 'react'; +import { Box, Typography } from '@mui/material'; +import { ReviewStates } from '@/config/macros/uploadsystemmacros'; +import { UploadUpdateValidationsProps } from '@/config/macros/uploadsystemmacros'; +import { useOrgCensusContext, usePlotContext } from '@/app/contexts/userselectionprovider'; export default function UploadUpdateValidations(props: Readonly) { const { setReviewState, schema } = props; const [isUpdateValidationComplete, setIsUpdateValidationComplete] = useState(false); const [countdown, setCountdown] = useState(5); - const [ellipsis, setEllipsis] = useState(""); + const [ellipsis, setEllipsis] = useState(''); const currentPlot = usePlotContext(); const currentCensus = useOrgCensusContext(); @@ -20,7 +20,7 @@ export default function UploadUpdateValidations(props: Readonly { if (!isUpdateValidationComplete) { const ellipsisTimer = setInterval(() => { - setEllipsis(prev => (prev.length < 4 ? prev + "." : "")); + setEllipsis(prev => (prev.length < 4 ? prev + '.' : '')); }, 500); return () => clearInterval(ellipsisTimer); @@ -51,14 +51,14 @@ export default function UploadUpdateValidations(props: Readonly + {!isUpdateValidationComplete ? ( @@ -67,15 +67,15 @@ export default function UploadUpdateValidations(props: Readonly Complete! - Continuing to Azure upload in {countdown} seconds. + Continuing to Azure upload in {countdown} seconds. )} diff --git a/frontend/components/uploadsystem/segments/uploadvalidation.tsx b/frontend/components/uploadsystem/segments/uploadvalidation.tsx index 292ff6ad..dc187896 100644 --- a/frontend/components/uploadsystem/segments/uploadvalidation.tsx +++ b/frontend/components/uploadsystem/segments/uploadvalidation.tsx @@ -1,11 +1,11 @@ -"use client"; -import React, { useEffect, useState } from "react"; -import { Box, LinearProgress, Typography } from "@mui/material"; -import { ReviewStates } from "@/config/macros/uploadsystemmacros"; -import { UploadValidationProps } from "@/config/macros/uploadsystemmacros"; -import { ValidationResponse } from "@/components/processors/processormacros"; -import CircularProgress from "@mui/joy/CircularProgress"; -import { useOrgCensusContext, usePlotContext } from "@/app/contexts/userselectionprovider"; +'use client'; +import React, { useEffect, useState } from 'react'; +import { Box, LinearProgress, Typography } from '@mui/material'; +import { ReviewStates } from '@/config/macros/uploadsystemmacros'; +import { UploadValidationProps } from '@/config/macros/uploadsystemmacros'; +import { ValidationResponse } from '@/components/processors/processormacros'; +import CircularProgress from '@mui/joy/CircularProgress'; +import { useOrgCensusContext, usePlotContext } from '@/app/contexts/userselectionprovider'; type ValidationMessages = { [key: string]: string; @@ -29,18 +29,18 @@ const UploadValidation: React.FC = ({ setReviewState, sch }; useEffect(() => { - console.log("Loading Validations..."); - fetch("/api/validations/validationlist", { method: "GET" }) + console.log('Loading Validations...'); + fetch('/api/validations/validationlist', { method: 'GET' }) .then(response => response.json()) .then(data => { - console.log("Validation Messages:", data); + console.log('Validation Messages:', data); setValidationMessages(data); const initialProgress = Object.keys(data).reduce((acc, api) => ({ ...acc, [api]: 0 }), {}); setValidationProgress(initialProgress); - console.log("Initial Progress:", initialProgress); + console.log('Initial Progress:', initialProgress); }) .catch(error => { - console.error("Error fetching validation messages:", error); + console.error('Error fetching validation messages:', error); }); }, []); @@ -52,21 +52,21 @@ const UploadValidation: React.FC = ({ setReviewState, sch const showNextPrompt = async (index: number, foundError: boolean = false) => { console.log(`showNextPrompt called with index: ${index}, foundError: ${foundError}`); - console.log("Current validationMessages length:", Object.keys(validationMessages).length); + console.log('Current validationMessages length:', Object.keys(validationMessages).length); if (index >= Object.keys(validationMessages).length) { setIsValidationComplete(true); setErrorsFound(foundError); - console.log("Validation Complete. Errors Found:", foundError); + console.log('Validation Complete. Errors Found:', foundError); return; } const api = Object.keys(validationMessages)[index]; - console.log("Performing Validation for:", api); + console.log('Performing Validation for:', api); try { const { response, hasError } = await performValidation(api); - console.log(`Validation ${api} Result:`, response, "Has Error:", hasError); + console.log(`Validation ${api} Result:`, response, 'Has Error:', hasError); setValidationResults(prevResults => ({ ...prevResults, @@ -81,20 +81,20 @@ const UploadValidation: React.FC = ({ setReviewState, sch }; const performValidation = async (api: string): Promise<{ response: ValidationResponse; hasError: boolean }> => { - console.log("Performing validation for:", api); - console.log("Current Plot:", currentPlot); - console.log("Current Census:", currentCensus); + console.log('Performing validation for:', api); + console.log('Current Plot:', currentPlot); + console.log('Current Census:', currentCensus); let queryParams = `schema=${schema}&plotID=${currentPlot?.id}&censusID=${currentCensus?.dateRanges[0].censusID}`; - if (["ValidateScreenMeasuredDiameterMinMax", "ValidateHOMUpperAndLowerBounds"].includes(api)) { + if (['ValidateScreenMeasuredDiameterMinMax', 'ValidateHOMUpperAndLowerBounds'].includes(api)) { const values = minMaxValues[api] || defaultMinMaxValues[api]; queryParams += `&minValue=${values.min}&maxValue=${values.max}`; } try { - console.log("Validation URL:", `/api/validations/${api}?${queryParams}`); + console.log('Validation URL:', `/api/validations/${api}?${queryParams}`); const response = await fetch(`/api/validations/${api}?${queryParams}`); - console.log("Response Status:", response.status); + console.log('Response Status:', response.status); if (!response.ok) { console.log(`Response not OK for ${api}:`, response.statusText); throw new Error(`Error executing ${api}`); @@ -140,20 +140,20 @@ const UploadValidation: React.FC = ({ setReviewState, sch {Object.keys(validationMessages).length > 0 && ( {!isValidationComplete ? ( Validating data... @@ -162,17 +162,17 @@ const UploadValidation: React.FC = ({ setReviewState, sch ) : ( @@ -195,7 +195,7 @@ const UploadValidation: React.FC = ({ setReviewState, sch {result.failedRows > 0 ? ( <> - {result.message} - Failed Core Measurement IDs: {result.failedCoreMeasurementIDs?.join(", ") ?? "None"} + Failed Core Measurement IDs: {result.failedCoreMeasurementIDs?.join(', ') ?? 'None'} ) : ( diff --git a/frontend/components/uploadsystem/uploadparent.tsx b/frontend/components/uploadsystem/uploadparent.tsx index cb4f2298..86eba344 100644 --- a/frontend/components/uploadsystem/uploadparent.tsx +++ b/frontend/components/uploadsystem/uploadparent.tsx @@ -1,23 +1,23 @@ -"use client"; -import React, { useEffect, useState } from "react"; -import { CMError } from "@/config/macros/uploadsystemmacros"; -import { ReviewProgress, ReviewStates } from "@/config/macros/uploadsystemmacros"; -import { FileCollectionRowSet, FileRow, FileRowSet, getTableHeaders, RequiredTableHeadersByFormType } from "@/config/macros/formdetails"; -import { FileWithPath } from "react-dropzone"; -import { useOrgCensusContext, usePlotContext, useSiteContext } from "@/app/contexts/userselectionprovider"; -import { useSession } from "next-auth/react"; -import { parse, ParseResult } from "papaparse"; -import { Box, Typography } from "@mui/joy"; -import UploadParseFiles from "@/components/uploadsystem/segments/uploadparsefiles"; -import UploadReviewFiles from "@/components/uploadsystem/segments/uploadreviewfiles"; -import UploadFireSQL from "@/components/uploadsystem/segments/uploadfiresql"; -import UploadError from "@/components/uploadsystem/segments/uploaderror"; -import UploadValidation from "@/components/uploadsystem/segments/uploadvalidation"; -import UploadUpdateValidations from "@/components/uploadsystem/segments/uploadupdatevalidations"; -import UploadStart from "@/components/uploadsystem/segments/uploadstart"; -import UploadFireAzure from "@/components/uploadsystem/segments/uploadfireazure"; -import UploadComplete from "@/components/uploadsystem/segments/uploadcomplete"; -import moment from "moment"; +'use client'; +import React, { useEffect, useState } from 'react'; +import { CMError } from '@/config/macros/uploadsystemmacros'; +import { ReviewProgress, ReviewStates } from '@/config/macros/uploadsystemmacros'; +import { FileCollectionRowSet, FileRow, FileRowSet, getTableHeaders, RequiredTableHeadersByFormType } from '@/config/macros/formdetails'; +import { FileWithPath } from 'react-dropzone'; +import { useOrgCensusContext, usePlotContext, useSiteContext } from '@/app/contexts/userselectionprovider'; +import { useSession } from 'next-auth/react'; +import { parse, ParseResult } from 'papaparse'; +import { Box, Typography } from '@mui/joy'; +import UploadParseFiles from '@/components/uploadsystem/segments/uploadparsefiles'; +import UploadReviewFiles from '@/components/uploadsystem/segments/uploadreviewfiles'; +import UploadFireSQL from '@/components/uploadsystem/segments/uploadfiresql'; +import UploadError from '@/components/uploadsystem/segments/uploaderror'; +import UploadValidation from '@/components/uploadsystem/segments/uploadvalidation'; +import UploadUpdateValidations from '@/components/uploadsystem/segments/uploadupdatevalidations'; +import UploadStart from '@/components/uploadsystem/segments/uploadstart'; +import UploadFireAzure from '@/components/uploadsystem/segments/uploadfireazure'; +import UploadComplete from '@/components/uploadsystem/segments/uploadcomplete'; +import moment from 'moment'; export interface CMIDRow { coreMeasurementID: number; @@ -46,11 +46,11 @@ export default function UploadParent(props: UploadParentProps) { * this will be the new parent upload function that will then pass data to child components being called within */ // select schema table that file should be uploaded to --> state - const [uploadForm, setUploadForm] = useState(""); - const [personnelRecording, setPersonnelRecording] = useState(""); - const [coordUnit, setCoordUnit] = useState(""); - const [dbhUnit, setDBHUnit] = useState(""); - const [homUnit, setHOMUnit] = useState(""); + const [uploadForm, setUploadForm] = useState(''); + const [personnelRecording, setPersonnelRecording] = useState(''); + const [coordUnit, setCoordUnit] = useState(''); + const [dbhUnit, setDBHUnit] = useState(''); + const [homUnit, setHOMUnit] = useState(''); // core enum to handle state progression const [reviewState, setReviewState] = useState(ReviewStates.UPLOAD_FILES); @@ -66,20 +66,20 @@ export default function UploadParent(props: UploadParentProps) { const [confirmationDialogOpen, setConfirmationDialogOpen] = React.useState(false); const [currentFileHeaders, setCurrentFileHeaders] = useState([]); const [expectedHeaders, setExpectedHeaders] = useState([]); - const [uploadCompleteMessage, setUploadCompleteMessage] = useState(""); + const [uploadCompleteMessage, setUploadCompleteMessage] = useState(''); const [allFileHeaders, setAllFileHeaders] = useState<{ [key: string]: string[]; }>({}); const [isDataUnsaved, setIsDataUnsaved] = useState(false); const [uploadError, setUploadError] = useState(); - const [errorComponent, setErrorComponent] = useState(""); + const [errorComponent, setErrorComponent] = useState(''); const [allRowToCMID, setAllRowToCMID] = useState([]); const [progressTracker, setProgressTracker] = useState(ReviewProgress.START); const [cmErrors, setCMErrors] = useState([]); const currentPlot = usePlotContext(); const currentCensus = useOrgCensusContext(); const currentSite = useSiteContext(); - if (!currentSite) throw new Error("site must be selected!"); + if (!currentSite) throw new Error('site must be selected!'); const { data: session } = useSession(); useEffect(() => { @@ -94,14 +94,14 @@ export default function UploadParent(props: UploadParentProps) { const handleBeforeUnload = (event: BeforeUnloadEvent) => { if (isDataUnsaved) { event.preventDefault(); // Required to standardize behavior across browsers - event.returnValue = ""; // In modern browsers, the message is not customizable but setting returnValue is necessary + event.returnValue = ''; // In modern browsers, the message is not customizable but setting returnValue is necessary } }; - window.addEventListener("beforeunload", handleBeforeUnload); + window.addEventListener('beforeunload', handleBeforeUnload); return () => { - window.removeEventListener("beforeunload", handleBeforeUnload); + window.removeEventListener('beforeunload', handleBeforeUnload); }; }, [isDataUnsaved]); // Run the effect when isDataUnsaved changes @@ -127,14 +127,14 @@ export default function UploadParent(props: UploadParentProps) { setParsedData({}); setErrors({}); setErrorRows({}); - setUploadForm(""); - setPersonnelRecording(""); + setUploadForm(''); + setPersonnelRecording(''); setReviewState(ReviewStates.START); } async function resetError() { setUploadError(null); - setErrorComponent(""); + setErrorComponent(''); } async function handleConfirmationApproval() { @@ -188,8 +188,8 @@ export default function UploadParent(props: UploadParentProps) { const parseFile = async (file: FileWithPath) => { try { const fileText = await file.text(); - const isCSV = file.name.endsWith(".csv"); - const delimiter = isCSV ? "," : "\t"; + const isCSV = file.name.endsWith('.csv'); + const delimiter = isCSV ? ',' : '\t'; parse(fileText, { delimiter: delimiter, @@ -197,18 +197,18 @@ export default function UploadParent(props: UploadParentProps) { skipEmptyLines: true, transformHeader: h => h.trim(), transform: (value, field) => { - if (uploadForm === "measurements" && field === "date") { + if (uploadForm === 'measurements' && field === 'date') { const match = value.match(/(\d{4})[\/.-](\d{1,2})[\/.-](\d{1,2})|(\d{1,2})[\/.-](\d{1,2})[\/.-](\d{4})/); if (match) { let normalizedDate; if (match[1]) { - normalizedDate = `${match[1]}-${match[2].padStart(2, "0")}-${match[3].padStart(2, "0")}`; + normalizedDate = `${match[1]}-${match[2].padStart(2, '0')}-${match[3].padStart(2, '0')}`; } else { - normalizedDate = `${match[6]}-${match[5].padStart(2, "0")}-${match[4].padStart(2, "0")}`; + normalizedDate = `${match[6]}-${match[5].padStart(2, '0')}-${match[4].padStart(2, '0')}`; } - const parsedDate = moment(normalizedDate, "YYYY-MM-DD", true); + const parsedDate = moment(normalizedDate, 'YYYY-MM-DD', true); if (parsedDate.isValid()) { return parsedDate.toDate(); } else { @@ -244,8 +244,8 @@ export default function UploadParent(props: UploadParentProps) { for (const header of requiredHeaders) { const value = row[header.label]; - if (value === null || value === undefined || value === "" || value === "NULL") { - rowErrors[header.label] = "This field is required"; + if (value === null || value === undefined || value === '' || value === 'NULL') { + rowErrors[header.label] = 'This field is required'; hasError = true; } } @@ -281,7 +281,7 @@ export default function UploadParent(props: UploadParentProps) { originalError: error }; setUploadError(errorWithFile); - setErrorComponent("UploadParseFiles"); + setErrorComponent('UploadParseFiles'); setReviewState(ReviewStates.ERRORS); } }; @@ -308,7 +308,7 @@ export default function UploadParent(props: UploadParentProps) { }, [progressTracker, reviewState]); const renderStateContent = () => { - if (uploadForm === "" && reviewState !== ReviewStates.START) handleReturnToStart().catch(console.error); + if (uploadForm === '' && reviewState !== ReviewStates.START) handleReturnToStart().catch(console.error); switch (reviewState) { case ReviewStates.START: return ( @@ -380,7 +380,7 @@ export default function UploadParent(props: UploadParentProps) { parsedData={parsedData} setReviewState={setReviewState} setIsDataUnsaved={setIsDataUnsaved} - schema={currentSite.schemaName || ""} + schema={currentSite.schemaName || ''} uploadCompleteMessage={uploadCompleteMessage} setUploadCompleteMessage={setUploadCompleteMessage} setUploadError={setUploadError} @@ -389,9 +389,9 @@ export default function UploadParent(props: UploadParentProps) { /> ); case ReviewStates.VALIDATE: - return ; + return ; case ReviewStates.UPDATE: - return ; + return ; case ReviewStates.UPLOAD_AZURE: return ( ); @@ -427,17 +427,17 @@ export default function UploadParent(props: UploadParentProps) { {currentCensus && currentPlot && ( - + Drag and drop files into the box to upload them to storage - - {renderStateContent()} + + {renderStateContent()} {/* diff --git a/frontend/components/uploadsystemhelpers/displayparseddatagrid.tsx b/frontend/components/uploadsystemhelpers/displayparseddatagrid.tsx index ef919cd6..21ac2cd1 100644 --- a/frontend/components/uploadsystemhelpers/displayparseddatagrid.tsx +++ b/frontend/components/uploadsystemhelpers/displayparseddatagrid.tsx @@ -1,20 +1,20 @@ -"use client"; +'use client'; -import { Box, Paper, Typography } from "@mui/material"; -import React, { Dispatch, SetStateAction, useState, useEffect } from "react"; -import { FileWithPath } from "react-dropzone"; -import "@/styles/validationtable.css"; -import moment from "moment"; -import { GridCellParams, GridColDef, GridRowModel, GridRowsProp } from "@mui/x-data-grid"; -import { StyledDataGrid } from "@/config/styleddatagrid"; -import { FileErrors, FileRow, FileCollectionRowSet, RowValidationErrors, ValidationFunction, getTableHeaders } from "@/config/macros/formdetails"; -import { validateAttributesRow } from "@/config/sqlrdsdefinitions/tables/attributerds"; -import { validatePersonnelRow } from "@/config/sqlrdsdefinitions/tables/personnelrds"; -import { validateQuadratsRow } from "@/config/sqlrdsdefinitions/tables/quadratrds"; -import { validateSpeciesFormRow } from "@/config/sqlrdsdefinitions/tables/speciesrds"; -import { validateSubquadratsRow } from "@/config/sqlrdsdefinitions/tables/subquadratrds"; -import { validateMeasurementsRow } from "@/config/sqlrdsdefinitions/views/measurementssummaryviewrds"; -import { usePlotContext } from "@/app/contexts/userselectionprovider"; +import { Box, Paper, Typography } from '@mui/material'; +import React, { Dispatch, SetStateAction, useState, useEffect } from 'react'; +import { FileWithPath } from 'react-dropzone'; +import '@/styles/validationtable.css'; +import moment from 'moment'; +import { GridCellParams, GridColDef, GridRowModel, GridRowsProp } from '@mui/x-data-grid'; +import { StyledDataGrid } from '@/config/styleddatagrid'; +import { FileErrors, FileRow, FileCollectionRowSet, RowValidationErrors, ValidationFunction, getTableHeaders } from '@/config/macros/formdetails'; +import { validateAttributesRow } from '@/config/sqlrdsdefinitions/tables/attributerds'; +import { validatePersonnelRow } from '@/config/sqlrdsdefinitions/tables/personnelrds'; +import { validateQuadratsRow } from '@/config/sqlrdsdefinitions/tables/quadratrds'; +import { validateSpeciesFormRow } from '@/config/sqlrdsdefinitions/tables/speciesrds'; +import { validateSubquadratsRow } from '@/config/sqlrdsdefinitions/tables/subquadratrds'; +import { validateMeasurementsRow } from '@/config/sqlrdsdefinitions/views/measurementssummaryviewrds'; +import { usePlotContext } from '@/app/contexts/userselectionprovider'; export interface ValidationTableProps { uploadedData: FileWithPath[]; @@ -77,49 +77,49 @@ export const DisplayParsedDataGridInline: React.FC = (pr headerName: header.label, flex: 1, getCellClassName: (params: GridCellParams) => { - const rowIndex = params.id.toString().split("-")[1]; + const rowIndex = params.id.toString().split('-')[1]; const errorKey = `row-${rowIndex}`; const cellError = errors[fileName]?.[errorKey]?.[header.label]; - return cellError ? "error-cell" : ""; + return cellError ? 'error-cell' : ''; }, renderCell: (params: GridCellParams) => { - if (header.label === "date") { - const formattedDate = params.value ? moment(params.value).format("YYYY-MM-DD") : ""; - return {formattedDate}; + if (header.label === 'date') { + const formattedDate = params.value ? moment(params.value).format('YYYY-MM-DD') : ''; + return {formattedDate}; } - const rowIndex = params.id.toString().split("-")[1]; + const rowIndex = params.id.toString().split('-')[1]; const errorKey = `row-${rowIndex}`; const cellError = errors[fileName]?.[errorKey]?.[header.label]; // Extract the display value const displayValue = params.value; const isAutoFillCorrection = - cellError && (cellError === "Genus was auto-filled based on species field." || cellError === "Species field was split into genus and species."); + cellError && (cellError === 'Genus was auto-filled based on species field.' || cellError === 'Species field was split into genus and species.'); return ( {cellError ? ( <> {isAutoFillCorrection ? ( - {displayValue !== undefined && displayValue !== null ? displayValue.toString() : ""} + {displayValue !== undefined && displayValue !== null ? displayValue.toString() : ''} ) : ( <> - {displayValue !== undefined && displayValue !== null ? displayValue.toString() : ""} + {displayValue !== undefined && displayValue !== null ? displayValue.toString() : ''} NULL )} ) : ( - - {displayValue !== undefined && displayValue !== null ? displayValue.toString() : ""} + + {displayValue !== undefined && displayValue !== null ? displayValue.toString() : ''} )} @@ -134,23 +134,23 @@ export const DisplayParsedDataGridInline: React.FC = (pr const tempErrors: FileErrors = {}; Object.entries(singleFileData).forEach(([rowKey, rowData], index) => { - if (typeof rowData === "object" && rowData !== null) { + if (typeof rowData === 'object' && rowData !== null) { const row = { id: `${fileName}-${index}`, ...rowData } as FileRow; let rowErrors = validateRowByFormType(formType, row); // Check species/genus condition independently - if (formType === "species") { - const speciesField = row["species"]; - const genusField = row["genus"]; + if (formType === 'species') { + const speciesField = row['species']; + const genusField = row['genus']; if (speciesField && !genusField) { const speciesWords = speciesField.trim().split(/\s+/); if (speciesWords.length === 2) { const [genus, species] = speciesWords; - row["genus"] = genus; - row["species"] = species; + row['genus'] = genus; + row['species'] = species; rowErrors = rowErrors || {}; - rowErrors["genus"] = "Genus was auto-filled based on species field."; - rowErrors["species"] = "Species field was split into genus and species."; + rowErrors['genus'] = 'Genus was auto-filled based on species field.'; + rowErrors['species'] = 'Species field was split into genus and species.'; } } } @@ -213,10 +213,10 @@ export const DisplayParsedDataGridInline: React.FC = (pr ); return ( - + {validRows.length > 0 && ( = (pr } }} autoHeight - getRowHeight={() => "auto"} + getRowHeight={() => 'auto'} /> )} {invalidRows.length > 0 && ( @@ -237,12 +237,12 @@ export const DisplayParsedDataGridInline: React.FC = (pr The following rows have been autocorrected to fit the schema. Please review the corrected rows and make sure they are correct.
- Red-highlighted text indicates invalid values that were detected and will be replaced with NULL. + Red-highlighted text indicates invalid values that were detected and will be replaced with NULL.
- Light blue text indicates values that were auto-filled or auto-corrected based on other fields. + Light blue text indicates values that were auto-filled or auto-corrected based on other fields.
= (pr } }} autoHeight - getRowHeight={() => "auto"} + getRowHeight={() => 'auto'} />
)} diff --git a/frontend/components/uploadsystemhelpers/dropzone.tsx b/frontend/components/uploadsystemhelpers/dropzone.tsx index ad94743b..54a8c010 100644 --- a/frontend/components/uploadsystemhelpers/dropzone.tsx +++ b/frontend/components/uploadsystemhelpers/dropzone.tsx @@ -1,12 +1,12 @@ -"use client"; -import React, { useCallback } from "react"; -import { DropzoneProps, DropzonePureProps } from "@/config/macros"; -import { FileRejection, FileWithPath, useDropzone } from "react-dropzone"; -import { parse, ParseConfig } from "papaparse"; -import { FileUploadIcon } from "@/components/icons"; +'use client'; +import React, { useCallback } from 'react'; +import { DropzoneProps, DropzonePureProps } from '@/config/macros'; +import { FileRejection, FileWithPath, useDropzone } from 'react-dropzone'; +import { parse, ParseConfig } from 'papaparse'; +import { FileUploadIcon } from '@/components/icons'; -import "@/styles/dropzone.css"; -import { subtitle } from "@/config/primitives"; +import '@/styles/dropzone.css'; +import { subtitle } from '@/config/primitives'; /** * This is the presentation component for FileUploadComponents. @@ -15,19 +15,19 @@ import { subtitle } from "@/config/primitives"; export function DropzoneCoreDisplay({ getRootProps, getInputProps, isDragActive }: DropzonePureProps) { return ( <> -
+
-

- {" "} - {" "} +

+ {' '} + {' '}

{isDragActive ? ( -

+

Drop file here...

) : ( -

+

Choose a CSV or ArcGIS file or drag it here.

)} @@ -46,12 +46,12 @@ export function DropzoneLogic({ onChange }: DropzoneProps) { acceptedFiles.forEach((file: FileWithPath) => { const reader = new FileReader(); - reader.onabort = () => alert("file reading was aborted"); - reader.onerror = () => alert("file reading has failed"); + reader.onabort = () => alert('file reading was aborted'); + reader.onerror = () => alert('file reading has failed'); reader.onload = () => { // Do whatever you want with the file contents const binaryStr = reader.result as string; - const config: ParseConfig = { delimiter: "," }; + const config: ParseConfig = { delimiter: ',' }; const results = parse(binaryStr, config); if (results.errors.length) { @@ -63,7 +63,7 @@ export function DropzoneLogic({ onChange }: DropzoneProps) { }); onChange(acceptedFiles, rejectedFiles); rejectedFiles.forEach((fileRejection: FileRejection) => { - alert(" The file " + fileRejection.file.name + " was not uploaded. Only .csv and .xlsx files are supported."); + alert(' The file ' + fileRejection.file.name + ' was not uploaded. Only .csv and .xlsx files are supported.'); }); }, [onChange] @@ -71,9 +71,9 @@ export function DropzoneLogic({ onChange }: DropzoneProps) { const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop, accept: { - "text/csv": [".csv"], - "text/xlsx": [".xlsx"], - "text/text": [".txt"] + 'text/csv': ['.csv'], + 'text/xlsx': ['.xlsx'], + 'text/text': ['.txt'] } }); diff --git a/frontend/components/uploadsystemhelpers/filelist.tsx b/frontend/components/uploadsystemhelpers/filelist.tsx index 24ac2c15..d1a213bd 100644 --- a/frontend/components/uploadsystemhelpers/filelist.tsx +++ b/frontend/components/uploadsystemhelpers/filelist.tsx @@ -1,12 +1,12 @@ -"use client"; -import React from "react"; -import { FileListProps } from "@/config/macros/formdetails"; +'use client'; +import React from 'react'; +import { FileListProps } from '@/config/macros/formdetails'; -import "@/styles/dropzone.css"; -import { Card, CardContent, CardHeader, Pagination } from "@mui/material"; -import Chip from "@mui/joy/Chip"; -import Divider from "@mui/joy/Divider"; -import { Box, Stack } from "@mui/joy"; +import '@/styles/dropzone.css'; +import { Card, CardContent, CardHeader, Pagination } from '@mui/material'; +import Chip from '@mui/joy/Chip'; +import Divider from '@mui/joy/Divider'; +import { Box, Stack } from '@mui/joy'; /** * A simple list of files with their sizes. @@ -17,29 +17,29 @@ export function FileList(props: Readonly) { setDataViewActive(value); // Directly update the dataViewActive in the parent component }; return ( - + File Preview: - - - - + + + + File Name:
- - {acceptedFiles?.length > 0 && acceptedFiles[dataViewActive - 1].path ? acceptedFiles[dataViewActive - 1].path! : ""} + + {acceptedFiles?.length > 0 && acceptedFiles[dataViewActive - 1].path ? acceptedFiles[dataViewActive - 1].path! : ''}
- - + + File Size:
- - {acceptedFiles?.length > 0 && acceptedFiles[dataViewActive - 1].size ? acceptedFiles[dataViewActive - 1].size : ""} bytes + + {acceptedFiles?.length > 0 && acceptedFiles[dataViewActive - 1].size ? acceptedFiles[dataViewActive - 1].size : ''} bytes
- - {acceptedFiles.length > 1 && } + + {acceptedFiles.length > 1 && }
); diff --git a/frontend/components/uploadsystemhelpers/groupedformselection.tsx b/frontend/components/uploadsystemhelpers/groupedformselection.tsx index b7f25714..d9bf7b20 100644 --- a/frontend/components/uploadsystemhelpers/groupedformselection.tsx +++ b/frontend/components/uploadsystemhelpers/groupedformselection.tsx @@ -1,14 +1,14 @@ -import * as React from "react"; -import Select from "@mui/joy/Select"; -import Option, { optionClasses } from "@mui/joy/Option"; -import Chip from "@mui/joy/Chip"; -import List from "@mui/joy/List"; -import ListItemDecorator, { listItemDecoratorClasses } from "@mui/joy/ListItemDecorator"; -import ListDivider from "@mui/joy/ListDivider"; -import ListItem from "@mui/joy/ListItem"; -import Typography from "@mui/joy/Typography"; -import Check from "@mui/icons-material/Check"; -import { FormGroups, TableHeadersByFormType } from "@/config/macros/formdetails"; +import * as React from 'react'; +import Select from '@mui/joy/Select'; +import Option, { optionClasses } from '@mui/joy/Option'; +import Chip from '@mui/joy/Chip'; +import List from '@mui/joy/List'; +import ListItemDecorator, { listItemDecoratorClasses } from '@mui/joy/ListItemDecorator'; +import ListDivider from '@mui/joy/ListDivider'; +import ListItem from '@mui/joy/ListItem'; +import Typography from '@mui/joy/Typography'; +import Check from '@mui/icons-material/Check'; +import { FormGroups, TableHeadersByFormType } from '@/config/macros/formdetails'; interface SelectFormTypeProps { externalState: string; @@ -17,9 +17,9 @@ interface SelectFormTypeProps { } const SelectFormType: React.FC = ({ externalState, updateExternalState, updateExternalHeaders }) => { - const colors: Record = { - DatabaseForms: "neutral", - CTFSWebForms: "primary" + const colors: Record = { + DatabaseForms: 'neutral', + CTFSWebForms: 'primary' }; const handleChange = (event: React.SyntheticEvent | null, newValue: string | null) => { if (newValue) { @@ -33,23 +33,23 @@ const SelectFormType: React.FC = ({ externalState, updateEx placeholder="Choose a form" slotProps={{ listbox: { - component: "div", + component: 'div', sx: { maxHeight: 240, - overflow: "auto", - "--List-padding": "0px", - "--ListItem-radius": "0px" + overflow: 'auto', + '--List-padding': '0px', + '--ListItem-radius': '0px' } } }} value={externalState} onChange={handleChange} - sx={{ display: "flex", flex: 1 }} + sx={{ display: 'flex', flex: 1 }} > {Object.entries(FormGroups).map(([name, forms], index) => ( {index !== 0 && } - + {name} ({forms.length}) @@ -61,9 +61,9 @@ const SelectFormType: React.FC = ({ externalState, updateEx value={form} label={ - + {name} - {" "} + {' '} {form} } diff --git a/frontend/components/uploadsystemhelpers/progressstepper.tsx b/frontend/components/uploadsystemhelpers/progressstepper.tsx index 3970d982..3a83d9e1 100644 --- a/frontend/components/uploadsystemhelpers/progressstepper.tsx +++ b/frontend/components/uploadsystemhelpers/progressstepper.tsx @@ -1,11 +1,11 @@ -"use client"; +'use client'; -import React, { useEffect } from "react"; -import { ReviewProgress, ReviewStates } from "@/config/macros/uploadsystemmacros"; -import { ProgressStepperProps } from "@/config/macros/uploadsystemmacros"; -import { Step, stepClasses, StepIndicator, stepIndicatorClasses, Stepper, Typography } from "@mui/joy"; -import CheckRoundedIcon from "@mui/icons-material/CheckRounded"; -import KeyboardArrowDownRoundedIcon from "@mui/icons-material/KeyboardArrowDownRounded"; +import React, { useEffect } from 'react'; +import { ReviewProgress, ReviewStates } from '@/config/macros/uploadsystemmacros'; +import { ProgressStepperProps } from '@/config/macros/uploadsystemmacros'; +import { Step, stepClasses, StepIndicator, stepIndicatorClasses, Stepper, Typography } from '@mui/joy'; +import CheckRoundedIcon from '@mui/icons-material/CheckRounded'; +import KeyboardArrowDownRoundedIcon from '@mui/icons-material/KeyboardArrowDownRounded'; export default function ProgressStepper(props: Readonly) { const { progressTracker, reviewState, setProgressTracker } = props; @@ -46,9 +46,9 @@ export default function ProgressStepper(props: Readonly) { ) { }, [`& .${stepClasses.completed}`]: { [`& .${stepIndicatorClasses.root}`]: { - borderColor: "primary.300", - color: "primary.300" + borderColor: 'primary.300', + color: 'primary.300' }, - "&::after": { - bgcolor: "primary.300" + '&::after': { + bgcolor: 'primary.300' } }, [`& .${stepClasses.active}`]: { [`& .${stepIndicatorClasses.root}`]: { - borderColor: "currentColor" + borderColor: 'currentColor' } }, [`& .${stepClasses.disabled} *`]: { - color: "neutral.outlinedDisabledColor" + color: 'neutral.outlinedDisabledColor' } }} > @@ -80,7 +80,7 @@ export default function ProgressStepper(props: Readonly) { disabled={progressTracker < ReviewProgress.START} orientation="vertical" indicator={ - + {progressTracker === ReviewProgress.START ? : } } @@ -103,7 +103,7 @@ export default function ProgressStepper(props: Readonly) { disabled={progressTracker < ReviewProgress.UPLOAD_FILES} orientation="vertical" indicator={ - + {progressTracker === ReviewProgress.UPLOAD_FILES ? : } } @@ -126,7 +126,7 @@ export default function ProgressStepper(props: Readonly) { disabled={progressTracker < ReviewProgress.REVIEW} orientation="vertical" indicator={ - + {progressTracker === ReviewProgress.REVIEW ? : } } @@ -149,7 +149,7 @@ export default function ProgressStepper(props: Readonly) { disabled={progressTracker < ReviewProgress.UPLOAD_SQL} orientation="vertical" indicator={ - + {progressTracker === ReviewProgress.UPLOAD_SQL ? : } } @@ -172,7 +172,7 @@ export default function ProgressStepper(props: Readonly) { disabled={progressTracker < ReviewProgress.VALIDATE} orientation="vertical" indicator={ - + {progressTracker === ReviewProgress.VALIDATE ? : } } @@ -195,7 +195,7 @@ export default function ProgressStepper(props: Readonly) { disabled={progressTracker < ReviewProgress.VALIDATE_ERRORS_FOUND} orientation="vertical" indicator={ - + {progressTracker === ReviewProgress.VALIDATE_ERRORS_FOUND ? : } } @@ -218,7 +218,7 @@ export default function ProgressStepper(props: Readonly) { disabled={progressTracker < ReviewProgress.UPDATE} orientation="vertical" indicator={ - + {progressTracker === ReviewProgress.UPDATE ? : } } @@ -241,7 +241,7 @@ export default function ProgressStepper(props: Readonly) { disabled={progressTracker < ReviewProgress.UPLOAD_AZURE} orientation="vertical" indicator={ - + {progressTracker === ReviewProgress.UPLOAD_AZURE ? : } } @@ -264,7 +264,7 @@ export default function ProgressStepper(props: Readonly) { disabled={progressTracker < ReviewProgress.COMPLETE} orientation="vertical" indicator={ - + {progressTracker === ReviewProgress.COMPLETE ? : } } diff --git a/frontend/components/uploadsystemhelpers/uploadparentmodal.tsx b/frontend/components/uploadsystemhelpers/uploadparentmodal.tsx index 8c043479..c5bc31aa 100644 --- a/frontend/components/uploadsystemhelpers/uploadparentmodal.tsx +++ b/frontend/components/uploadsystemhelpers/uploadparentmodal.tsx @@ -1,9 +1,9 @@ -"use client"; +'use client'; -import { IconButton, Modal, ModalDialog } from "@mui/joy"; -import CloseIcon from "@mui/icons-material/Close"; +import { IconButton, Modal, ModalDialog } from '@mui/joy'; +import CloseIcon from '@mui/icons-material/Close'; -import UploadParent from "../uploadsystem/uploadparent"; +import UploadParent from '../uploadsystem/uploadparent'; interface UPMProps { isUploadModalOpen: boolean; @@ -21,10 +21,10 @@ export default function UploadParentModal(props: UPMProps) { open={isUploadModalOpen} onClose={() => {}} aria-labelledby="upload-dialog-title" - sx={{ display: "flex", alignItems: "center", justifyContent: "center" }} + sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }} > - - + + diff --git a/frontend/components/uploadsystemhelpers/uploadvalidationerrordisplay.tsx b/frontend/components/uploadsystemhelpers/uploadvalidationerrordisplay.tsx index eee94d8e..321a1f1b 100644 --- a/frontend/components/uploadsystemhelpers/uploadvalidationerrordisplay.tsx +++ b/frontend/components/uploadsystemhelpers/uploadvalidationerrordisplay.tsx @@ -1,14 +1,14 @@ -"use client"; - -import React, { useEffect, useState } from "react"; -import { Box, Pagination, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography } from "@mui/material"; -import { formatDate } from "@/config/macros"; -import { CMError } from "@/config/macros/uploadsystemmacros"; -import { ReviewStates } from "@/config/macros/uploadsystemmacros"; -import { UploadValidationErrorDisplayProps } from "@/config/macros/uploadsystemmacros"; -import { FileRow } from "@/config/macros/formdetails"; -import { DetailedCMIDRow } from "@/components/uploadsystem/uploadparent"; -import { Button, CircularProgress } from "@mui/joy"; +'use client'; + +import React, { useEffect, useState } from 'react'; +import { Box, Pagination, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography } from '@mui/material'; +import { formatDate } from '@/config/macros'; +import { CMError } from '@/config/macros/uploadsystemmacros'; +import { ReviewStates } from '@/config/macros/uploadsystemmacros'; +import { UploadValidationErrorDisplayProps } from '@/config/macros/uploadsystemmacros'; +import { FileRow } from '@/config/macros/formdetails'; +import { DetailedCMIDRow } from '@/components/uploadsystem/uploadparent'; +import { Button, CircularProgress } from '@mui/joy'; function isFileRowKey(key: string, cmidRow: DetailedCMIDRow): boolean { return key in cmidRow.row; @@ -38,9 +38,9 @@ const UploadValidationErrorDisplay: React.FC const fetchData = async () => { try { setIsLoading(true); - const response = await fetch("/api/validations/validationerrordisplay"); + const response = await fetch('/api/validations/validationerrordisplay'); if (!response.ok) { - throw new Error("Error fetching CMError data"); + throw new Error('Error fetching CMError data'); } const data = await response.json(); setCMErrors(data); @@ -60,40 +60,40 @@ const UploadValidationErrorDisplay: React.FC // Updated tableHeaders to include new fields const tableHeaders = [ - { key: "tag", header: "Tree" }, - { key: "stemtag", header: "Stem" }, - { key: "speciesName", header: "Species" }, // for detailedCMIDRow - { key: "plotName", header: "Plot" }, // for detailedCMIDRow - { key: "plotCensusNumber", header: "Plot Census" }, // for detailedCMIDRow - { key: "quadratName", header: "Quadrat" }, // for detailedCMIDRow - { key: "censusStart", header: "Census Start" }, // for detailedCMIDRow - { key: "date", header: "Date" }, - { key: "censusEnd", header: "Census End" }, // for detailedCMIDRow - { key: "lx", header: "X-coord" }, - { key: "ly", header: "Y-coord" }, - { key: "personnelName", header: "Personnel" }, // for detailedCMIDRow - { key: "dbh", header: "Diameter at Breast Height" }, - { key: "hom", header: "Height of Measure" }, - { key: "codes", header: "Attributes" } + { key: 'tag', header: 'Tree' }, + { key: 'stemtag', header: 'Stem' }, + { key: 'speciesName', header: 'Species' }, // for detailedCMIDRow + { key: 'plotName', header: 'Plot' }, // for detailedCMIDRow + { key: 'plotCensusNumber', header: 'Plot Census' }, // for detailedCMIDRow + { key: 'quadratName', header: 'Quadrat' }, // for detailedCMIDRow + { key: 'censusStart', header: 'Census Start' }, // for detailedCMIDRow + { key: 'date', header: 'Date' }, + { key: 'censusEnd', header: 'Census End' }, // for detailedCMIDRow + { key: 'lx', header: 'X-coord' }, + { key: 'ly', header: 'Y-coord' }, + { key: 'personnelName', header: 'Personnel' }, // for detailedCMIDRow + { key: 'dbh', header: 'Diameter at Breast Height' }, + { key: 'hom', header: 'Height of Measure' }, + { key: 'codes', header: 'Attributes' } ]; const errorMapping: { [key: string]: string[] } = { - "1": ["codes"], - "2": ["dbh"], - "3": ["hom"], - "4": ["tag", "stemtag"], - "5": ["tag", "stemtag", "quadratName"], - "6": ["lx", "ly"], - "7": ["speciesName"], - "8": ["date"], - "9": ["tag", "stemtag", "plotCensusNumber"], - "10": ["tag", "stemtag", "plotCensusNumber"], - "11": ["quadratName"], - "12": ["speciesName"], - "13": ["dbh"], - "14": ["dbh"], - "15": ["tag"], - "16": ["quadratName"] + '1': ['codes'], + '2': ['dbh'], + '3': ['hom'], + '4': ['tag', 'stemtag'], + '5': ['tag', 'stemtag', 'quadratName'], + '6': ['lx', 'ly'], + '7': ['speciesName'], + '8': ['date'], + '9': ['tag', 'stemtag', 'plotCensusNumber'], + '10': ['tag', 'stemtag', 'plotCensusNumber'], + '11': ['quadratName'], + '12': ['speciesName'], + '13': ['dbh'], + '14': ['dbh'], + '15': ['tag'], + '16': ['quadratName'] }; const getCellStyles = (cmError: CMError, key: string) => { @@ -105,7 +105,7 @@ const UploadValidationErrorDisplay: React.FC shouldHighlight = true; } }); - return shouldHighlight ? { backgroundColor: "#eaf436", color: "black", fontWeight: "bold" } : {}; + return shouldHighlight ? { backgroundColor: '#eaf436', color: 'black', fontWeight: 'bold' } : {}; }; if (error) { @@ -135,7 +135,7 @@ const UploadValidationErrorDisplay: React.FC const cellStyle = cmError ? getCellStyles(cmError, itemKeyString) : undefined; return ( - {itemKey === "date" ? formatDate(cellValue) : cellValue} + {itemKey === 'date' ? formatDate(cellValue) : cellValue} ); }; @@ -149,17 +149,17 @@ const UploadValidationErrorDisplay: React.FC return ( Core Measurement Errors - +
{tableHeaders.map(item => ( @@ -182,9 +182,9 @@ const UploadValidationErrorDisplay: React.FC {description} @@ -199,7 +199,7 @@ const UploadValidationErrorDisplay: React.FC
-
diff --git a/frontend/components/uploadsystemhelpers/viewuploadedfiles.tsx b/frontend/components/uploadsystemhelpers/viewuploadedfiles.tsx index 21cb77e1..2a734de8 100644 --- a/frontend/components/uploadsystemhelpers/viewuploadedfiles.tsx +++ b/frontend/components/uploadsystemhelpers/viewuploadedfiles.tsx @@ -1,16 +1,16 @@ -"use client"; -import React, { Dispatch, SetStateAction, useCallback, useEffect, useState } from "react"; -import { tableHeaderSettings } from "@/config/macros"; -import { fileColumns } from "@/config/macros/formdetails"; -import { UploadedFileData } from "@/config/macros/formdetails"; -import { Plot } from "@/config/sqlrdsdefinitions/tables/plotrds"; -import { Button, Card, CardContent, CardHeader, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from "@mui/material"; -import { DeleteIcon, DownloadIcon, EditIcon } from "@/components/icons"; -import Divider from "@mui/joy/Divider"; -import CircularProgress from "@mui/joy/CircularProgress"; -import Box from "@mui/joy/Box"; -import Typography from "@mui/joy/Typography"; -import { OrgCensus } from "@/config/sqlrdsdefinitions/orgcensusrds"; +'use client'; +import React, { Dispatch, SetStateAction, useCallback, useEffect, useState } from 'react'; +import { tableHeaderSettings } from '@/config/macros'; +import { fileColumns } from '@/config/macros/formdetails'; +import { UploadedFileData } from '@/config/macros/formdetails'; +import { Plot } from '@/config/sqlrdsdefinitions/tables/plotrds'; +import { Button, Card, CardContent, CardHeader, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from '@mui/material'; +import { DeleteIcon, DownloadIcon, EditIcon } from '@/components/icons'; +import Divider from '@mui/joy/Divider'; +import CircularProgress from '@mui/joy/CircularProgress'; +import Box from '@mui/joy/Box'; +import Typography from '@mui/joy/Typography'; +import { OrgCensus } from '@/config/sqlrdsdefinitions/orgcensusrds'; // @todo: look into using an ID other than plot name. // @todo: react router URL params to pass in the ID for Browse. // https://reactrouter.com/en/main/start/tutorial#url-params-in-loaders @@ -27,11 +27,11 @@ function LoadingFiles(props: Readonly) { }, []); // on mount return ( - - - Accessing Container: {currentPlot?.plotName?.trim() ?? "none"}-{currentCensus?.plotCensusNumber?.toString() ?? "none"} + + + Accessing Container: {currentPlot?.plotName?.trim() ?? 'none'}-{currentCensus?.plotCensusNumber?.toString() ?? 'none'}
-
@@ -43,17 +43,17 @@ function LoadingFiles(props: Readonly) {
Loading Files...
- + - - + + Retrieving files... @@ -75,17 +75,17 @@ export default function ViewUploadedFiles(props: Readonly) { const [isLoaded, setIsLoaded] = useState(false); const [fileRows, setFileRows] = useState(); const [openSnackbar, setOpenSnackbar] = useState(false); - const [errorMessage, setErrorMessage] = useState(""); + const [errorMessage, setErrorMessage] = useState(''); const handleDownload = async (containerName: string, filename: string) => { try { const response = await fetch(`/api/filehandlers/downloadfile?container=${containerName.toLowerCase()}&filename=${encodeURIComponent(filename)}`); - if (!response.ok) throw new Error("Error getting download link"); + if (!response.ok) throw new Error('Error getting download link'); const data = await response.json(); window.location.href = data.url; // Navigates to the pre-signed URL } catch (error: any) { - console.error("Download error:", error); + console.error('Download error:', error); setErrorMessage(error.message); // Set the error message setOpenSnackbar(true); // Open the snackbar } @@ -94,14 +94,14 @@ export default function ViewUploadedFiles(props: Readonly) { const handleDelete = async (containerName: string, filename: string) => { try { const response = await fetch(`/api/filehandlers/deletefile?container=${containerName.toLowerCase()}&filename=${encodeURIComponent(filename)}`, { - method: "DELETE" + method: 'DELETE' }); - if (!response.ok) throw new Error("Error deleting file"); + if (!response.ok) throw new Error('Error deleting file'); // Refresh the file list after successful deletion setRefreshFileList(true); } catch (error: any) { - console.error("Delete error:", error); + console.error('Delete error:', error); setErrorMessage(error.message); // Set the error message setOpenSnackbar(true); // Open the snackbar } @@ -110,18 +110,18 @@ export default function ViewUploadedFiles(props: Readonly) { const getListOfFiles = useCallback(async () => { try { const response = await fetch( - `/api/filehandlers/downloadallfiles?plot=${currentPlot?.plotName?.trim() ?? "none"}&census=${currentCensus?.plotCensusNumber?.toString().trim() ?? "none"}`, + `/api/filehandlers/downloadallfiles?plot=${currentPlot?.plotName?.trim() ?? 'none'}&census=${currentCensus?.plotCensusNumber?.toString().trim() ?? 'none'}`, { - method: "GET" + method: 'GET' } ); if (!response.ok) { const jsonOutput = await response.json(); - console.error("response.statusText", jsonOutput.statusText); + console.error('response.statusText', jsonOutput.statusText); setErrorMessage(`API response: ${jsonOutput.statusText}`); } else { - console.log(response.status + ", " + response.statusText); + console.log(response.status + ', ' + response.statusText); const data = await response.json(); setFileRows(data.blobData); setIsLoaded(true); @@ -156,7 +156,7 @@ export default function ViewUploadedFiles(props: Readonly) { // Reset the error message after a short delay // This delay ensures that the user has enough time to see the error message const timer = setTimeout(() => { - setErrorMessage(""); + setErrorMessage(''); }, 6000); // Adjust the delay as needed // Clear the timer if the component unmounts @@ -168,30 +168,30 @@ export default function ViewUploadedFiles(props: Readonly) { return ; } else { const sortedFileData: UploadedFileData[] = fileRows; - if (fileRows.length > 1) sortedFileData.toSorted((a, b) => new Date(b.date ? b.date : "").getTime() - new Date(a.date ? a.date : "").getTime()); + if (fileRows.length > 1) sortedFileData.toSorted((a, b) => new Date(b.date ? b.date : '').getTime() - new Date(a.date ? a.date : '').getTime()); let i = 1; sortedFileData.forEach(row => { row.key = i; i++; }); - const sortedFileTextCSV = sortedFileData.filter(row => row.name.toLowerCase().endsWith(".csv") || row.name.toLowerCase().endsWith(".txt")); - const sortedFileArcGIS = sortedFileData.filter(row => row.name.toLowerCase().endsWith(".xlsx")); + const sortedFileTextCSV = sortedFileData.filter(row => row.name.toLowerCase().endsWith('.csv') || row.name.toLowerCase().endsWith('.txt')); + const sortedFileArcGIS = sortedFileData.filter(row => row.name.toLowerCase().endsWith('.xlsx')); return ( <> {/*CSV FILES*/} - - - - Accessing Container: {currentPlot?.plotName?.trim() ?? "none"}-{currentCensus?.plotCensusNumber?.toString() ?? "none"} + + + + Accessing Container: {currentPlot?.plotName?.trim() ?? 'none'}-{currentCensus?.plotCensusNumber?.toString() ?? 'none'} - - Uploaded CSV Files + Uploaded CSV Files - + - +
File Count @@ -220,8 +220,8 @@ export default function ViewUploadedFiles(props: Readonly) { sx={ errs ? { - color: "red", - fontWeight: "bold" + color: 'red', + fontWeight: 'bold' } : {} } @@ -232,8 +232,8 @@ export default function ViewUploadedFiles(props: Readonly) { sx={ errs ? { - color: "red", - fontWeight: "bold" + color: 'red', + fontWeight: 'bold' } : {} } @@ -244,8 +244,8 @@ export default function ViewUploadedFiles(props: Readonly) { sx={ errs ? { - color: "red", - fontWeight: "bold" + color: 'red', + fontWeight: 'bold' } : {} } @@ -256,8 +256,8 @@ export default function ViewUploadedFiles(props: Readonly) { sx={ errs ? { - color: "red", - fontWeight: "bold" + color: 'red', + fontWeight: 'bold' } : {} } @@ -268,8 +268,8 @@ export default function ViewUploadedFiles(props: Readonly) { sx={ errs ? { - color: "red", - fontWeight: "bold" + color: 'red', + fontWeight: 'bold' } : {} } @@ -280,19 +280,19 @@ export default function ViewUploadedFiles(props: Readonly) { sx={ errs ? { - color: "red", - fontWeight: "bold" + color: 'red', + fontWeight: 'bold' } : {} } > - {new Date(row.date ? row.date : "").toString()} + {new Date(row.date ? row.date : '').toString()}