diff --git a/packages/website/package.json b/packages/website/package.json index 85ef8ae4c..b43e80f7d 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -76,7 +76,8 @@ "axios": "axios/dist/node/axios.cjs", "^csv-stringify/browser/esm/sync": "/../../node_modules/csv-stringify/dist/cjs/sync.cjs" }, - "globalSetup": "./src/global-setup.js" + "globalSetup": "./src/global-setup.js", + "resetMocks": false }, "browserslist": { "production": [ diff --git a/packages/website/src/mocks/mockReefCheckSurvey.ts b/packages/website/src/mocks/mockReefCheckSurvey.ts new file mode 100644 index 000000000..127b0373d --- /dev/null +++ b/packages/website/src/mocks/mockReefCheckSurvey.ts @@ -0,0 +1,239 @@ +import { ReefCheckSurvey } from 'store/ReefCheckSurveys/types'; + +export const mockReefCheckSurvey: ReefCheckSurvey = { + id: '000887f9-b8c4-4c43-bc63-700c0e8b385b', + siteId: 1748, + reefCheckSiteId: '6a8719bb-91fe-46c1-9635-37fb1f022ea9', + date: '2016-04-20T13:30:00.000Z', + errors: 'FALSE', + depth: 3, + timeOfDayWorkBegan: ' 1:30 pm', + timeOfDayWorkEnded: ' 2:15 pm', + methodUsedToDetermineLocation: null, + riverMouthWidth: '<10m', + weather: 'Sunny', + airTemp: 31, + waterTempAtSurface: 30, + waterTempAt3M: 28, + waterTempAt10M: 28, + approxPopnSizeX1000: 6, + horizontalVisibilityInWater: 8, + bestReefArea: 'Yes', + whyWasThisSiteSelected: 'outside proposed MPA', + shelteredOrExposed: 'Exposed', + anyMajorStormsInLastYears: 'Yes', + whenStorms: null, + overallAnthroImpact: 'High', + whatKindOfImpacts: null, + siltation: 'Always', + dynamiteFishing: 'Low', + poisonFishing: 'Low', + aquariumFishCollection: 'Low', + harvestOfInvertsForFood: 'Medium', + harvestOfInvertsForCurio: 'Medium', + touristDivingSnorkeling: 'None', + sewagePollution: 'Medium', + industrialPollution: 'None', + commercialFishing: 'Low', + liveFoodFishing: 'Low', + artisinalRecreational: 'Low', + otherFormsOfFishing: null, + otherFishing: null, + yachts: 'None', + levelOfOtherImpacts: null, + otherImpacts: null, + isSiteProtected: 'FALSE', + isProtectionEnforced: null, + levelOfPoaching: null, + spearfishing: null, + bannedCommercialFishing: null, + recreationalFishing: null, + invertebrateShellCollection: null, + anchoring: null, + diving: null, + otherSpecify: null, + natureOfProtection: null, + siteComments: 'COTs present', + substrateComments: null, + fishComments: 'Coral recruits observed in area.', + invertsComments: null, + commentsFromOrganismSheet: null, + grouperSize: null, + percentBleaching: null, + percentColoniesBleached: null, + percentOfEachColony: null, + suspectedDisease: null, + rareAnimalsDetails: null, + submittedBy: null, + satelliteTemperature: 28.2, + organisms: [ + { + id: 1, + surveyId: '000887f9-b8c4-4c43-bc63-700c0e8b385b', + date: '2016-04-20T00:00:00.000Z', + organism: 'Butterflyfish', + type: 'Fish', + s1: 6, + s2: 10, + s3: 12, + s4: 0, + recordedBy: null, + errors: null, + }, + { + id: 2, + surveyId: '000887f9-b8c4-4c43-bc63-700c0e8b385b', + date: '2016-04-20T00:00:00.000Z', + organism: 'Haemulidae', + type: 'Fish', + s1: 0, + s2: 1, + s3: 0, + s4: 0, + recordedBy: null, + errors: null, + }, + { + id: 14, + surveyId: '000887f9-b8c4-4c43-bc63-700c0e8b385b', + date: '2016-04-20T00:00:00.000Z', + organism: 'Banded Coral Shrimp', + type: 'Invertebrate', + s1: 0, + s2: 0, + s3: 0, + s4: 1, + recordedBy: null, + errors: null, + }, + { + id: 15, + surveyId: '000887f9-b8c4-4c43-bc63-700c0e8b385b', + date: '2016-04-20T00:00:00.000Z', + organism: 'Diadema', + type: 'Invertebrate', + s1: 8, + s2: 14, + s3: 64, + s4: 32, + recordedBy: null, + errors: null, + }, + { + id: 16, + surveyId: '000887f9-b8c4-4c43-bc63-700c0e8b385b', + date: '2016-04-20T00:00:00.000Z', + organism: 'Pencil Urchin', + type: 'Invertebrate', + s1: 0, + s2: 0, + s3: 0, + s4: 0, + recordedBy: null, + errors: null, + }, + { + id: 29, + surveyId: '000887f9-b8c4-4c43-bc63-700c0e8b385b', + date: '2016-04-20T00:00:00.000Z', + organism: 'Coral Damage Anchor', + type: 'Impact', + s1: 0, + s2: 0, + s3: 0, + s4: 1, + recordedBy: null, + errors: null, + }, + { + id: 34, + surveyId: '000887f9-b8c4-4c43-bc63-700c0e8b385b', + date: '2016-04-20T00:00:00.000Z', + organism: 'Bleaching (% Of Population)', + type: 'Impact', + s1: 0, + s2: 0, + s3: 0, + s4: 0, + recordedBy: null, + errors: null, + }, + { + id: 39, + surveyId: '000887f9-b8c4-4c43-bc63-700c0e8b385b', + date: '2016-04-20T00:00:00.000Z', + organism: 'Turtles', + type: 'Rare Animal', + s1: 0, + s2: 1, + s3: 0, + s4: 0, + recordedBy: null, + errors: null, + }, + { + id: 40, + surveyId: '000887f9-b8c4-4c43-bc63-700c0e8b385b', + date: '2016-04-20T00:00:00.000Z', + organism: 'Mantas', + type: 'Rare Animal', + s1: 0, + s2: 0, + s3: 0, + s4: 0, + recordedBy: null, + errors: null, + }, + ], + substrates: [ + { + id: 501, + surveyId: '000887f9-b8c4-4c43-bc63-700c0e8b385b', + date: '2016-04-20T00:00:00.000Z', + substrateCode: 'HC', + s1: 22, + s2: 25, + s3: 27, + s4: 26, + recordedBy: null, + errors: null, + }, + { + id: 502, + surveyId: '000887f9-b8c4-4c43-bc63-700c0e8b385b', + date: '2016-04-20T00:00:00.000Z', + substrateCode: 'HC/B', + s1: 0, + s2: 0, + s3: 0, + s4: 0, + recordedBy: null, + errors: null, + }, + { + id: 503, + surveyId: '000887f9-b8c4-4c43-bc63-700c0e8b385b', + date: '2016-04-20T00:00:00.000Z', + substrateCode: 'HC/D', + s1: 0, + s2: 0, + s3: 0, + s4: 0, + recordedBy: null, + errors: null, + }, + ], + reefCheckSite: { + id: '6a8719bb-91fe-46c1-9635-37fb1f022ea9', + siteId: 1748, + reefName: 'Masigasig/Esteban Reef', + orientation: 'SE-NW', + country: 'Philippines', + stateProvinceIsland: 'Palawan', + cityTown: 'Taytay', + region: 'Indo-Pacific', + distanceFromShore: 127, + distanceFromNearestRiver: 0.5, + distanceToNearestPopn: 3, + }, +}; diff --git a/packages/website/src/routes/SiteRoutes/ReefCheckSurveys/ReefCheckSurveySummary.tsx b/packages/website/src/routes/SiteRoutes/ReefCheckSurveys/ReefCheckSurveySummary.tsx index 3afe15faa..c94695bf2 100644 --- a/packages/website/src/routes/SiteRoutes/ReefCheckSurveys/ReefCheckSurveySummary.tsx +++ b/packages/website/src/routes/SiteRoutes/ReefCheckSurveys/ReefCheckSurveySummary.tsx @@ -41,7 +41,7 @@ export const ReefCheckSurveySummaryComponent = ({ formatDate(survey?.date ?? '') )} - Reef Check + Reef Check Logo @@ -104,7 +104,7 @@ export const ReefCheckSurveySummaryComponent = ({ ); }; -const formatDate = (dateStr: string): string => { +export const formatDate = (dateStr: string): string => { const date = new Date(dateStr); return date.toLocaleTimeString([], { year: 'numeric', diff --git a/packages/website/src/routes/SiteRoutes/ReefCheckSurveys/ReefCheckSurveyTable/ReefCheckSurveyTable.test.tsx b/packages/website/src/routes/SiteRoutes/ReefCheckSurveys/ReefCheckSurveyTable/ReefCheckSurveyTable.test.tsx new file mode 100644 index 000000000..b97faaca1 --- /dev/null +++ b/packages/website/src/routes/SiteRoutes/ReefCheckSurveys/ReefCheckSurveyTable/ReefCheckSurveyTable.test.tsx @@ -0,0 +1,71 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import { ColumnDef, ReefCheckSurveyTable } from '.'; + +jest.mock('@material-ui/lab', () => ({ + Skeleton: 'mock-skeleton', +})); + +describe('ReefCheckSurveyTable', () => { + type MockDataItem = { + id: number; + name: string; + age: string; + }; + const mockTitle = 'title'; + const mockDescription = 'description'; + const mockColumns = [ + { header: 'Name', field: 'name' }, + { header: 'Age', field: 'age' }, + ] as ColumnDef[]; + const mockData = [ + { id: 1, name: 'Joe', age: '30' }, + { id: 2, name: 'Doe', age: '25' }, + ]; + + function renderReefCheckSurveyTable({ + loading, + data = mockData, + }: { loading?: boolean; data?: MockDataItem[] } = {}) { + return render( + , + ); + } + + it('should render correctly', () => { + const { getByText, container } = renderReefCheckSurveyTable(); + expect(getByText(mockTitle)).toBeInTheDocument(); + expect(getByText(mockDescription)).toBeInTheDocument(); + + const rows = container.querySelectorAll('mock-tablerow'); + expect(rows.length).toBe(3); // 1 header + 2 data + expect( + [...container.querySelectorAll('mock-tablecell').values()].map( + (el) => el.textContent, + ), + ).toEqual(['Name', 'Age', 'Joe', '30', 'Doe', '25']); + }); + + it('should render skeleton rows when loading', () => { + const { container } = renderReefCheckSurveyTable({ + loading: true, + data: [], + }); + const rows = container.querySelectorAll('mock-tablerow'); + expect(rows.length).toBe(4); // 1 header + 3 skeleton rows + rows.forEach((row, i) => { + if (i === 0) { + // skip header + return; + } + const cells = row.querySelectorAll('mock-skeleton'); + expect(cells.length).toBe(mockColumns.length); + }); + }); +}); diff --git a/packages/website/src/routes/SiteRoutes/ReefCheckSurveys/ReefCheckSurveyTable.tsx b/packages/website/src/routes/SiteRoutes/ReefCheckSurveys/ReefCheckSurveyTable/ReefCheckSurveyTable.tsx similarity index 99% rename from packages/website/src/routes/SiteRoutes/ReefCheckSurveys/ReefCheckSurveyTable.tsx rename to packages/website/src/routes/SiteRoutes/ReefCheckSurveys/ReefCheckSurveyTable/ReefCheckSurveyTable.tsx index 0d0546a0a..7774ab4ac 100644 --- a/packages/website/src/routes/SiteRoutes/ReefCheckSurveys/ReefCheckSurveyTable.tsx +++ b/packages/website/src/routes/SiteRoutes/ReefCheckSurveys/ReefCheckSurveyTable/ReefCheckSurveyTable.tsx @@ -23,7 +23,7 @@ export type ColumnDef = { } & TableCellProps; type ObjectWithId = { - id: string; + id: string | number; }; type ReefCheckSurveyTableIncomingProps = { diff --git a/packages/website/src/routes/SiteRoutes/ReefCheckSurveys/ReefCheckSurveyTable/index.ts b/packages/website/src/routes/SiteRoutes/ReefCheckSurveys/ReefCheckSurveyTable/index.ts new file mode 100644 index 000000000..42904bd53 --- /dev/null +++ b/packages/website/src/routes/SiteRoutes/ReefCheckSurveys/ReefCheckSurveyTable/index.ts @@ -0,0 +1 @@ +export * from './ReefCheckSurveyTable'; diff --git a/packages/website/src/routes/SiteRoutes/ReefCheckSurveys/index.test.tsx b/packages/website/src/routes/SiteRoutes/ReefCheckSurveys/index.test.tsx new file mode 100644 index 000000000..111c4060f --- /dev/null +++ b/packages/website/src/routes/SiteRoutes/ReefCheckSurveys/index.test.tsx @@ -0,0 +1,177 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import { MemoryRouter, Route, Routes } from 'react-router-dom'; +import { Provider } from 'react-redux'; +import configureStore from 'redux-mock-store'; +import { RootState } from 'store/configure'; +import { mockSite } from 'mocks/mockSite'; +import { mockReefCheckSurvey } from 'mocks/mockReefCheckSurvey'; +import { SelectedSiteState } from 'store/Sites/types'; +import { formatDate } from './ReefCheckSurveySummary'; +import { ReefCheckSurveyViewPage } from '.'; +import * as organismsTableModule from './ReefCheckSurveyOrganismsTable'; +import * as substratesModule from './ReefCheckSurveySubstratesTable'; + +jest.mock('common/NavBar', () => 'Mock-NavBar'); + +describe('ReefCheckSurveyViewPage', () => { + const mockStore = configureStore([]); + const reefCheckSurveyOrganismsTableSpy = jest.spyOn( + organismsTableModule, + 'ReefCheckSurveyOrganismsTable', + ); + + const reefCheckSurveySubstratesTableSpy = jest.spyOn( + substratesModule, + 'ReefCheckSurveySubstrates', + ); + + function renderReefCheckSurveyViewPage(error?: string) { + const store = mockStore({ + selectedSite: { details: mockSite, error } as SelectedSiteState, + reefCheckSurvey: { + loading: false, + survey: mockReefCheckSurvey, + }, + } as Partial); + const mockSiteId = 1; + const mockSurveyId = 1; + + store.dispatch = jest.fn(); + const renderResult = render( + + + + } + /> + + + , + ); + return { ...renderResult, store }; + } + + afterAll(() => { + reefCheckSurveyOrganismsTableSpy.mockRestore(); + reefCheckSurveySubstratesTableSpy.mockRestore(); + }); + + it('should dispatch reefCheckSurveyGetRequest and siteRequest on mount', () => { + const { store } = renderReefCheckSurveyViewPage(); + + expect(store.dispatch).toHaveBeenCalledTimes(2); + }); + + it('should render Not found if error is present in the store', () => { + const { getByAltText } = renderReefCheckSurveyViewPage('error'); + + expect(getByAltText('404 Not Found')).toBeInTheDocument(); + }); + + it('should render the NavBar component', () => { + const { container } = renderReefCheckSurveyViewPage(); + + expect(container.querySelector('mock-navbar')).toBeInTheDocument(); + }); + + it('should render a Button component with a link to the site page', () => { + const { getByRole } = renderReefCheckSurveyViewPage(); + + expect( + getByRole( + (role, element) => + role === 'button' && element?.textContent === 'Back to site', + ), + ).toHaveAttribute('href', `/sites/${mockSite.id}`); + }); + + describe('ReefCheckSurveySummary', () => { + it('should render summary', () => { + const { getByText, getByAltText } = renderReefCheckSurveyViewPage(); + const { reefName, region } = mockReefCheckSurvey.reefCheckSite ?? {}; + + expect( + getByText(formatDate(mockReefCheckSurvey.date)), + ).toBeInTheDocument(); + expect(getByAltText('Reef Check Logo')).toBeInTheDocument(); + expect(getByText(reefName ?? '')).toBeInTheDocument(); + expect(getByText(region ?? '')).toBeInTheDocument(); + expect( + getByText('Learn more about the data and how it’s collected'), + ).toHaveAttribute( + 'href', + 'https://www.reefcheck.org/tropical-program/tropical-monitoring-instruction/', + ); + expect(getByText('SATELLITE OBSERVATION')).toBeInTheDocument(); + expect( + getByText(`${mockReefCheckSurvey.satelliteTemperature} °C`), + ).toBeInTheDocument(); + }); + + it.todo('should request to download data when button is clicked'); + }); + + describe('ReefCheckSurveyDetails', () => { + it('should render survey details', () => { + const { getByText } = renderReefCheckSurveyViewPage(); + const { date } = mockReefCheckSurvey; + const displayDate = new Date(date).toLocaleDateString(); + + expect( + getByText(`${displayDate} REEF CHECK SURVEY DATA`), + ).toBeInTheDocument(); + }); + }); + + describe('ReefCheckSurveyOrganismsTable', () => { + beforeAll(() => renderReefCheckSurveyViewPage()); + + it('should render fish table', () => { + expect(reefCheckSurveyOrganismsTableSpy).toHaveBeenCalledWith( + expect.objectContaining({ title: 'Fish' }), + expect.anything(), + ); + }); + + it('should render invertebrate table', () => { + expect(reefCheckSurveyOrganismsTableSpy).toHaveBeenCalledWith( + expect.objectContaining({ title: 'Invertebrate' }), + expect.anything(), + ); + }); + + it('should render impact table', () => { + expect(reefCheckSurveyOrganismsTableSpy).toHaveBeenCalledWith( + expect.objectContaining({ title: 'Impact' }), + expect.anything(), + ); + }); + + it('should render bleaching and coral diseases table', () => { + expect(reefCheckSurveyOrganismsTableSpy).toHaveBeenCalledWith( + expect.objectContaining({ title: 'Bleaching and Coral Diseases' }), + expect.anything(), + ); + }); + + it('should render rare animal table', () => { + expect(reefCheckSurveyOrganismsTableSpy).toHaveBeenCalledWith( + expect.objectContaining({ title: 'Rare Animal' }), + expect.anything(), + ); + }); + + it('should render reef structure and composition table', () => { + expect(reefCheckSurveySubstratesTableSpy).toHaveBeenCalledWith( + expect.objectContaining({ title: 'Reef Structure and Composition' }), + expect.anything(), + ); + }); + }); +}); diff --git a/packages/website/src/routes/SiteRoutes/ReefCheckSurveys/index.tsx b/packages/website/src/routes/SiteRoutes/ReefCheckSurveys/index.tsx index edb1da313..2e0109ba3 100644 --- a/packages/website/src/routes/SiteRoutes/ReefCheckSurveys/index.tsx +++ b/packages/website/src/routes/SiteRoutes/ReefCheckSurveys/index.tsx @@ -1,20 +1,21 @@ import React, { useEffect } from 'react'; -import { Box, Button, Grid, Typography } from '@material-ui/core'; +import { useSelector, useDispatch } from 'react-redux'; import { Link, useParams } from 'react-router-dom'; +import { Box, Button, Grid, Typography } from '@material-ui/core'; +import { ArrowBack } from '@material-ui/icons'; import { reefCheckSurveyGetRequest } from 'store/ReefCheckSurveys/reefCheckSurveySlice'; -import { useSelector, useDispatch } from 'react-redux'; import { siteErrorSelector, siteRequest } from 'store/Sites/selectedSiteSlice'; import NavBar from 'common/NavBar'; -import { ArrowBack } from '@material-ui/icons'; -import { ReefCheckSurveyOrganismsTable } from './ReefCheckSurveyOrganismsTable'; -import { ReefCheckSurveySummary } from './ReefCheckSurveySummary'; -import { ReefCheckSurveyDetails } from './ReefCheckSurveyDetails'; +import NotFound from 'routes/NotFound'; import { fishColumns } from './colDefs/fish.colDef'; import { invertebratesColumns } from './colDefs/invertables.colDef'; import { impactColumns } from './colDefs/impact.colDef'; import { rareAnimalsColumns } from './colDefs/rareAnimals.colDef'; import { bleachingColumns } from './colDefs/bleaching.colDef'; import { reefStructureColumns } from './colDefs/reefStructure'; +import { ReefCheckSurveyOrganismsTable } from './ReefCheckSurveyOrganismsTable'; +import { ReefCheckSurveySummary } from './ReefCheckSurveySummary'; +import { ReefCheckSurveyDetails } from './ReefCheckSurveyDetails'; import { ReefCheckSurveySubstrates } from './ReefCheckSurveySubstratesTable'; const impactRows = [ @@ -37,7 +38,12 @@ export const ReefCheckSurveyViewPage = () => { }, [dispatch, siteId, surveyId]); if (error) { - return Error loading site details; + return ( + <> + + + + ); } return ( <> diff --git a/packages/website/src/store/ReefCheckSurveys/types.ts b/packages/website/src/store/ReefCheckSurveys/types.ts index d51ab9c70..716bf93d6 100644 --- a/packages/website/src/store/ReefCheckSurveys/types.ts +++ b/packages/website/src/store/ReefCheckSurveys/types.ts @@ -2,7 +2,7 @@ import { Site } from 'store/Sites/types'; export interface ReefCheckSite { id: string; - siteId: string; + siteId: number; site?: Site; reefName: string | null; orientation: string | null; @@ -17,7 +17,7 @@ export interface ReefCheckSite { } export interface ReefCheckOrganism { - id: string; + id: number; date: string; surveyId: string; organism: string; @@ -31,7 +31,7 @@ export interface ReefCheckOrganism { } export interface ReefCheckSubstrate { - id: string; + id: number; surveyId: string; date: string; substrateCode: string; @@ -52,67 +52,67 @@ export interface ReefCheckSurvey { organisms: ReefCheckOrganism[]; substrates: ReefCheckSubstrate[]; date: string; - errors: string; - depth: number; - timeOfDayWorkBegan: string; - timeOfDayWorkEnded: string; - satelliteTemperature: number | null; - methodUsedToDetermineLocation: string; - riverMouthWidth: string; - weather: string; - airTemp: number; - waterTempAtSurface: number; - waterTempAt3M: number; - waterTempAt10M: number; - approxPopnSizeX1000: number; - horizontalVisibilityInWater: number; - bestReefArea: string; - whyWasThisSiteSelected: string; - shelteredOrExposed: string; - anyMajorStormsInLastYears: string; - whenStorms: string; - overallAnthroImpact: string; - whatKindOfImpacts: string; - siltation: string; - dynamiteFishing: string; - poisonFishing: string; - aquariumFishCollection: string; - harvestOfInvertsForFood: string; - harvestOfInvertsForCurio: string; - touristDivingSnorkeling: string; - sewagePollution: string; - industrialPollution: string; - commercialFishing: string; - liveFoodFishing: string; - artisinalRecreational: string; - otherFormsOfFishing: string; - otherFishing: string; - yachts: string; - levelOfOtherImpacts: string; - otherImpacts: string; - isSiteProtected: string; - isProtectionEnforced: string; - levelOfPoaching: string; - spearfishing: string; - bannedCommercialFishing: string; - recreationalFishing: string; - invertebrateShellCollection: string; - anchoring: string; - diving: string; - otherSpecify: string; - natureOfProtection: string; - siteComments: string; - substrateComments: string; - fishComments: string; - invertsComments: string; - commentsFromOrganismSheet: string; - grouperSize: string; - percentBleaching: string; - percentColoniesBleached: string; - percentOfEachColony: string; - suspectedDisease: string; - rareAnimalsDetails: string; - submittedBy: string; + errors: string | null; + depth: number | null; + timeOfDayWorkBegan: string | null; + timeOfDayWorkEnded: string | null; + satelliteTemperature: number | null | null; + methodUsedToDetermineLocation: string | null; + riverMouthWidth: string | null; + weather: string | null; + airTemp: number | null; + waterTempAtSurface: number | null; + waterTempAt3M: number | null; + waterTempAt10M: number | null; + approxPopnSizeX1000: number | null; + horizontalVisibilityInWater: number | null; + bestReefArea: string | null; + whyWasThisSiteSelected: string | null; + shelteredOrExposed: string | null; + anyMajorStormsInLastYears: string | null; + whenStorms: string | null; + overallAnthroImpact: string | null; + whatKindOfImpacts: string | null; + siltation: string | null; + dynamiteFishing: string | null; + poisonFishing: string | null; + aquariumFishCollection: string | null; + harvestOfInvertsForFood: string | null; + harvestOfInvertsForCurio: string | null; + touristDivingSnorkeling: string | null; + sewagePollution: string | null; + industrialPollution: string | null; + commercialFishing: string | null; + liveFoodFishing: string | null; + artisinalRecreational: string | null; + otherFormsOfFishing: string | null; + otherFishing: string | null; + yachts: string | null; + levelOfOtherImpacts: string | null; + otherImpacts: string | null; + isSiteProtected: string | null; + isProtectionEnforced: string | null; + levelOfPoaching: string | null; + spearfishing: string | null; + bannedCommercialFishing: string | null; + recreationalFishing: string | null; + invertebrateShellCollection: string | null; + anchoring: string | null; + diving: string | null; + otherSpecify: string | null; + natureOfProtection: string | null; + siteComments: string | null; + substrateComments: string | null; + fishComments: string | null; + invertsComments: string | null; + commentsFromOrganismSheet: string | null; + grouperSize: string | null; + percentBleaching: string | null; + percentColoniesBleached: string | null; + percentOfEachColony: string | null; + suspectedDisease: string | null; + rareAnimalsDetails: string | null; + submittedBy: string | null; } export interface ReefCheckSurveyState {