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()}