diff --git a/package-lock.json b/package-lock.json index 725dc65d..4731aa2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "lodash": "^4.17.21", "react": "^18.2.0", "react-redux": "^8.1.3", - "terraso-backend": "github:techmatters/terraso-backend#f671497", + "terraso-backend": "github:techmatters/terraso-backend#2ffcc08", "uuid": "^9.0.1" }, "devDependencies": { @@ -13098,7 +13098,7 @@ }, "node_modules/terraso-backend": { "version": "0.1.0", - "resolved": "git+ssh://git@github.com/techmatters/terraso-backend.git#f67149781a7fd91b896d30e40b355fbd4811091d" + "resolved": "git+ssh://git@github.com/techmatters/terraso-backend.git#2ffcc08ec645af449c3e758b62effd49c286aea6" }, "node_modules/test-exclude": { "version": "6.0.0", diff --git a/package.json b/package.json index e4da1aca..e97dac7a 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "lodash": "^4.17.21", "react": "^18.2.0", "react-redux": "^8.1.3", - "terraso-backend": "github:techmatters/terraso-backend#f671497", + "terraso-backend": "github:techmatters/terraso-backend#2ffcc08", "uuid": "^9.0.1" }, "scripts": { diff --git a/src/project/projectFragments.ts b/src/project/projectFragments.ts index 29dcba9c..a04c330b 100644 --- a/src/project/projectFragments.ts +++ b/src/project/projectFragments.ts @@ -45,8 +45,10 @@ export const projectData = /* GraphQL */ ` name privacy description + siteInstructions updatedAt archived + measurementUnits membershipList { ...projectMembershipList } diff --git a/src/project/projectService.ts b/src/project/projectService.ts index 73e34ea7..7588dab3 100644 --- a/src/project/projectService.ts +++ b/src/project/projectService.ts @@ -70,6 +70,7 @@ export const collapseProject = ({ return { project: { ...project, + siteInstructions: project.siteInstructions || undefined, sites: collapseToSet(Object.keys(sites)), memberships, }, diff --git a/src/project/projectSlice.ts b/src/project/projectSlice.ts index 6b67fc35..8251c9cb 100644 --- a/src/project/projectSlice.ts +++ b/src/project/projectSlice.ts @@ -22,6 +22,7 @@ import { updateUsers, } from 'terraso-client-shared/account/accountSlice'; import { + MeasurementUnits, ProjectAddUserMutationInput, UserRole, } from 'terraso-client-shared/graphqlSchema/graphql'; @@ -44,10 +45,12 @@ export type Project = { name: string; privacy: 'PRIVATE' | 'PUBLIC'; description: string; + siteInstructions?: string; updatedAt: string; // this should be Date.toLocaleDateString; redux can't serialize Dates memberships: Record; sites: SerializableSet; archived: boolean; + measurementUnits: MeasurementUnits; }; interface MembershipKey { diff --git a/src/selectors.test.ts b/src/selectors.test.ts index e021e6a6..07f57836 100644 --- a/src/selectors.test.ts +++ b/src/selectors.test.ts @@ -52,6 +52,8 @@ const generateProject = ( sites: siteSet, archived: false, memberships: keyBy(memberships, 'id'), + measurementUnits: 'METRIC', + siteInstructions: '', }; }; @@ -67,6 +69,7 @@ const generateSite = (project?: Project): Site => { privacy: 'PRIVATE', archived: false, updatedAt: '2023-10-24', + notes: {}, }; if (project !== undefined) { project.sites[site.id] = true; diff --git a/src/site/siteFragments.ts b/src/site/siteFragments.ts index e2c09e03..ee096747 100644 --- a/src/site/siteFragments.ts +++ b/src/site/siteFragments.ts @@ -30,5 +30,29 @@ export const siteData = /* GraphQL */ ` project { id } + notes { + edges { + node { + ...siteNoteData + } + } + } + } +`; + +export const siteNoteData = /* GraphQL */ ` + fragment siteNoteData on SiteNoteNode { + id + content + createdAt + updatedAt + author { + id + firstName + lastName + } + site { + id + } } `; diff --git a/src/site/siteService.ts b/src/site/siteService.ts index d2c87c11..9b3ca208 100644 --- a/src/site/siteService.ts +++ b/src/site/siteService.ts @@ -20,10 +20,13 @@ import { graphql } from 'terraso-client-shared/graphqlSchema'; import type { SiteAddMutationInput, SiteDataFragment, + SiteNoteAddMutationInput, + SiteNoteDataFragment, + SiteNoteUpdateMutationInput, SiteTransferMutationInput, SiteUpdateMutationInput, } from 'terraso-client-shared/graphqlSchema/graphql'; -import type { Site } from 'terraso-client-shared/site/siteSlice'; +import type { Site, SiteNote } from 'terraso-client-shared/site/siteSlice'; import * as terrasoApi from 'terraso-client-shared/terrasoApi/api'; import { collapseEdges, @@ -31,18 +34,41 @@ import { } from 'terraso-client-shared/terrasoApi/utils'; export const collapseSite = (site: SiteDataFragment): Site => { - const { project, owner, ...rest } = site; + const { project, owner, notes, ...rest } = site; return { ...rest, projectId: project?.id, ownerId: owner?.id, + notes: collapseSiteNotes(notes), }; }; + export const collapseSites = (sites: Connection) => Object.fromEntries( collapseEdges(sites).map(site => [site.id, collapseSite(site)]), ); +export const collapseSiteNotes = ( + siteNotes: Connection, +) => + Object.fromEntries( + collapseEdges(siteNotes).map(siteNote => [ + siteNote.id, + collapseSiteNote(siteNote), + ]), + ); + +export const collapseSiteNote = (siteNote: SiteNoteDataFragment): SiteNote => { + const { author, site, ...rest } = siteNote; + return { + ...rest, + authorId: author.id, + authorFirstName: author.firstName, + authorLastName: author.lastName, + siteId: site.id, + }; +}; + export const fetchSite = (id: string) => { const query = graphql(` query site($id: ID!) { @@ -194,3 +220,51 @@ export const transferSitesToProject = (input: SiteTransferMutationInput) => { }, ); }; + +export const addSiteNote = (siteNote: SiteNoteAddMutationInput) => { + const query = graphql(` + mutation addSiteNote($input: SiteNoteAddMutationInput!) { + addSiteNote(input: $input) { + siteNote { + ...siteNoteData + } + errors + } + } + `); + + return terrasoApi + .requestGraphQL(query, { input: siteNote }) + .then(resp => resp.addSiteNote.siteNote!); +}; + +export const deleteSiteNote = (siteNote: SiteNote) => { + const query = graphql(` + mutation deleteSiteNote($input: SiteNoteDeleteMutationInput!) { + deleteSiteNote(input: $input) { + errors + } + } + `); + + return terrasoApi + .requestGraphQL(query, { input: { id: siteNote.id } }) + .then(_ => siteNote); +}; + +export const updateSiteNote = (siteNote: SiteNoteUpdateMutationInput) => { + const query = graphql(` + mutation updateSiteNote($input: SiteNoteUpdateMutationInput!) { + updateSiteNote(input: $input) { + siteNote { + ...siteNoteData + } + errors + } + } + `); + + return terrasoApi + .requestGraphQL(query, { input: siteNote }) + .then(resp => resp.updateSiteNote.siteNote!); +}; diff --git a/src/site/siteSlice.ts b/src/site/siteSlice.ts index 0a361e59..fa4bd786 100644 --- a/src/site/siteSlice.ts +++ b/src/site/siteSlice.ts @@ -18,6 +18,8 @@ import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { SiteAddMutationInput, + SiteNoteAddMutationInput, + SiteNoteUpdateMutationInput, SiteTransferMutationInput, SiteUpdateMutationInput, } from 'terraso-client-shared/graphqlSchema/graphql'; @@ -41,6 +43,18 @@ export type Site = { privacy: SitePrivacy; archived: boolean; updatedAt: string; + notes: Record; +}; + +export type SiteNote = { + id: string; + siteId: string; + content: string; + createdAt: string; + updatedAt: string; + authorId: string; + authorFirstName: string; + authorLastName: string; }; const initialState = { @@ -110,6 +124,30 @@ export const transferSites = createAsyncThunk< return result; }); +export const addSiteNote = createAsyncThunk( + 'site/addSiteNote', + async (siteNote, _) => { + let result = await siteService.addSiteNote(siteNote); + return siteService.collapseSiteNote(result); + }, +); + +export const deleteSiteNote = createAsyncThunk( + 'site/deleteSiteNote', + async siteNote => { + let result = await siteService.deleteSiteNote(siteNote); + return result; + }, +); + +export const updateSiteNote = createAsyncThunk< + SiteNote, + SiteNoteUpdateMutationInput +>('site/updateSiteNote', async (siteNote, _) => { + let result = await siteService.updateSiteNote(siteNote); + return siteService.collapseSiteNote(result); +}); + const siteSlice = createSlice({ name: 'site', initialState, @@ -170,6 +208,24 @@ const siteSlice = createSlice({ } }, ); + + builder.addCase(addSiteNote.fulfilled, (state, { payload: siteNote }) => { + state.sites[siteNote.siteId].notes[siteNote.id] = siteNote; + }); + + builder.addCase( + deleteSiteNote.fulfilled, + (state, { payload: siteNote }) => { + delete state.sites[siteNote.siteId].notes[siteNote.id]; + }, + ); + + builder.addCase( + updateSiteNote.fulfilled, + (state, { payload: siteNote }) => { + state.sites[siteNote.siteId].notes[siteNote.id] = siteNote; + }, + ); }, });