From 7d262e9f587efc795778bfeac6e0746eef16bdd3 Mon Sep 17 00:00:00 2001 From: Kurt Hutten Date: Fri, 25 Dec 2020 17:29:01 +1100 Subject: [PATCH] Add Privacy Policy related improvements various thing to make sure we're GDPR, et al compliant --- .env.defaults | 4 + api/package.json | 3 +- .../README.md | 71 +++++++ .../schema.prisma | 93 +++++++++ .../steps.json | 176 ++++++++++++++++++ api/prisma/migrations/migrate.lock | 3 +- api/prisma/schema.prisma | 22 ++- api/src/graphql/subjectAccessRequests.sdl.js | 39 ++++ api/src/graphql/users.sdl.js | 1 + api/src/services/helpers.js | 18 ++ api/src/services/parts/parts.js | 11 +- .../subjectAccessRequests.js | 42 +++++ .../subjectAccessRequests.test.js | 9 + api/src/services/users/users.js | 14 +- redwood.toml | 2 +- web/src/Routes.js | 4 + web/src/components/Breadcrumb/Breadcrumb.js | 9 +- .../EditSubjectAccessRequestCell.js | 70 +++++++ .../components/EditUserCell/EditUserCell.js | 4 +- web/src/components/Footer/Footer.js | 16 ++ web/src/components/Footer/Footer.stories.js | 7 + web/src/components/Footer/Footer.test.js | 11 ++ web/src/components/IdeToolbar/IdeToolbar.js | 1 - .../components/ImageUploader/ImageUploader.js | 7 +- web/src/components/InputText/InputText.js | 15 +- web/src/components/LoginModal/LoginModal.js | 8 + web/src/components/PartProfile/PartProfile.js | 25 ++- web/src/components/Parts/Parts.js | 2 - .../SubjectAccessRequest.js | 119 ++++++++++++ .../SubjectAccessRequestCell.js | 22 +++ .../SubjectAccessRequestForm.js | 83 +++++++++ .../SubjectAccessRequests.js | 137 ++++++++++++++ .../SubjectAccessRequestsCell.js | 24 +++ web/src/components/UserCell/UserCell.js | 23 --- web/src/components/UserProfile/UserProfile.js | 33 ++-- web/src/index.html | 2 +- web/src/layouts/MainLayout/MainLayout.js | 25 +-- .../SubjectAccessRequestsLayout.js | 20 ++ .../CodeOfConductPage/CodeOfConductPage.js | 1 + .../EditSubjectAccessRequestPage.js | 12 ++ web/src/pages/EditUserPage/EditUserPage.js | 2 +- web/src/pages/IdePartPage/IdePartPage.js | 2 +- .../PrivacyPolicyPage/PrivacyPolicyPage.js | 153 +++++++++++++++ .../PrivacyPolicyPage.stories.js | 7 + .../PrivacyPolicyPage.test.js | 11 ++ .../SubjectAccessRequestPage.js | 12 ++ .../SubjectAccessRequestPage.stories.js | 7 + .../SubjectAccessRequestPage.test.js | 11 ++ .../SubjectAccessRequestsPage.js | 164 ++++++++++++++++ web/src/pages/UserPage/UserPage.js | 4 +- yarn.lock | 14 +- 51 files changed, 1480 insertions(+), 95 deletions(-) create mode 100644 api/prisma/migrations/20201227195638-add-subject-access-request-table/README.md create mode 100644 api/prisma/migrations/20201227195638-add-subject-access-request-table/schema.prisma create mode 100644 api/prisma/migrations/20201227195638-add-subject-access-request-table/steps.json create mode 100644 api/src/graphql/subjectAccessRequests.sdl.js create mode 100644 api/src/services/subjectAccessRequests/subjectAccessRequests.js create mode 100644 api/src/services/subjectAccessRequests/subjectAccessRequests.test.js create mode 100644 web/src/components/EditSubjectAccessRequestCell/EditSubjectAccessRequestCell.js create mode 100644 web/src/components/Footer/Footer.js create mode 100644 web/src/components/Footer/Footer.stories.js create mode 100644 web/src/components/Footer/Footer.test.js create mode 100644 web/src/components/SubjectAccessRequest/SubjectAccessRequest.js create mode 100644 web/src/components/SubjectAccessRequestCell/SubjectAccessRequestCell.js create mode 100644 web/src/components/SubjectAccessRequestForm/SubjectAccessRequestForm.js create mode 100644 web/src/components/SubjectAccessRequests/SubjectAccessRequests.js create mode 100644 web/src/components/SubjectAccessRequestsCell/SubjectAccessRequestsCell.js delete mode 100644 web/src/components/UserCell/UserCell.js create mode 100644 web/src/layouts/SubjectAccessRequestsLayout/SubjectAccessRequestsLayout.js create mode 100644 web/src/pages/EditSubjectAccessRequestPage/EditSubjectAccessRequestPage.js create mode 100644 web/src/pages/PrivacyPolicyPage/PrivacyPolicyPage.js create mode 100644 web/src/pages/PrivacyPolicyPage/PrivacyPolicyPage.stories.js create mode 100644 web/src/pages/PrivacyPolicyPage/PrivacyPolicyPage.test.js create mode 100644 web/src/pages/SubjectAccessRequestPage/SubjectAccessRequestPage.js create mode 100644 web/src/pages/SubjectAccessRequestPage/SubjectAccessRequestPage.stories.js create mode 100644 web/src/pages/SubjectAccessRequestPage/SubjectAccessRequestPage.test.js create mode 100644 web/src/pages/SubjectAccessRequestsPage/SubjectAccessRequestsPage.js diff --git a/.env.defaults b/.env.defaults index bc5fb1d..175fb7d 100644 --- a/.env.defaults +++ b/.env.defaults @@ -8,3 +8,7 @@ DATABASE_URL=file:./dev.db # disables Prisma CLI update notifier PRISMA_HIDE_UPDATE_MESSAGE=true + +CLOUDINARY_API_KEY=476712943135152 +# ask Kurt for help getting set up with a secret +# CLOUDINARY_API_SECRET= diff --git a/api/package.json b/api/package.json index 844989e..cf3e712 100644 --- a/api/package.json +++ b/api/package.json @@ -3,6 +3,7 @@ "version": "0.0.0", "private": true, "dependencies": { - "@redwoodjs/api": "^0.20.0" + "@redwoodjs/api": "^0.20.0", + "cloudinary": "^1.23.0" } } diff --git a/api/prisma/migrations/20201227195638-add-subject-access-request-table/README.md b/api/prisma/migrations/20201227195638-add-subject-access-request-table/README.md new file mode 100644 index 0000000..75d3f2d --- /dev/null +++ b/api/prisma/migrations/20201227195638-add-subject-access-request-table/README.md @@ -0,0 +1,71 @@ +# Migration `20201227195638-add-subject-access-request-table` + +This migration has been generated by Kurt Hutten at 12/28/2020, 6:56:38 AM. +You can check out the [state of the schema](./schema.prisma) after the migration. + +## Database Steps + +```sql +CREATE TABLE "SubjectAccessRequest" ( + "id" TEXT NOT NULL, + "comment" TEXT NOT NULL, + "payload" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + + FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE, +PRIMARY KEY ("id") +) +``` + +## Changes + +```diff +diff --git schema.prisma schema.prisma +migration 20201213004819-add-delete-on-part..20201227195638-add-subject-access-request-table +--- datamodel.dml ++++ datamodel.dml +@@ -1,7 +1,7 @@ + datasource DS { + provider = ["sqlite", "postgresql"] +- url = "***" ++ url = "***" + } + generator client { + provider = "prisma-client-js" +@@ -30,13 +30,14 @@ + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +- image String? // url maybe id or file storage service? cloudinary? +- bio String? //mark down +- Part Part[] +- Reaction PartReaction[] +- Comment Comment[] ++ image String? // url maybe id or file storage service? cloudinary? ++ bio String? //mark down ++ Part Part[] ++ Reaction PartReaction[] ++ Comment Comment[] ++ SubjectAccessRequest SubjectAccessRequest[] + } + model Part { + id String @id @default(uuid()) +@@ -78,4 +79,15 @@ + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + } ++ ++model SubjectAccessRequest { ++ id String @id @default(uuid()) ++ comment String ++ payload String // json dump ++ user User @relation(fields: [userId], references: [id]) ++ userId String ++ ++ createdAt DateTime @default(now()) ++ updatedAt DateTime @updatedAt ++} +``` + + diff --git a/api/prisma/migrations/20201227195638-add-subject-access-request-table/schema.prisma b/api/prisma/migrations/20201227195638-add-subject-access-request-table/schema.prisma new file mode 100644 index 0000000..c64ba45 --- /dev/null +++ b/api/prisma/migrations/20201227195638-add-subject-access-request-table/schema.prisma @@ -0,0 +1,93 @@ +datasource DS { + provider = ["sqlite", "postgresql"] + url = "***" +} + +generator client { + provider = "prisma-client-js" + binaryTargets = ["native", "rhel-openssl-1.0.x"] +} + +// sqlLight does not suport enums so we can't use enums until we set up postgresql in dev mode +// enum Role { +// USER +// ADMIN +// } + +// enum PartType { +// CASCADESTUDIO +// JSCAD +// } + +model User { + id String @id @default(uuid()) + userName String @unique // reffered to as userId in @relations + email String @unique + name String? + // role should probably be a list [] and also use enums, neither are supported by sqllight, so we need to set up postgresql in dev + // maybe let netlify handle roles for now. + // role String @default("user") + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + image String? // url maybe id or file storage service? cloudinary? + bio String? //mark down + Part Part[] + Reaction PartReaction[] + Comment Comment[] + SubjectAccessRequest SubjectAccessRequest[] +} + +model Part { + id String @id @default(uuid()) + title String + description String? // markdown string + code String? + mainImage String? // link to cloudinary + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + user User @relation(fields: [userId], references: [id]) + userId String + deleted Boolean @default(false) + + Comment Comment[] + Reaction PartReaction[] + @@unique([title, userId]) +} + +model PartReaction { + id String @id @default(uuid()) + emote String // an emoji + user User @relation(fields: [userId], references: [id]) + userId String + part Part @relation(fields: [partId], references: [id]) + partId String + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + @@unique([emote, userId, partId]) +} + +model Comment { + id String @id @default(uuid()) + text String // the comment, should I allow mark down? + user User @relation(fields: [userId], references: [id]) + userId String + part Part @relation(fields: [partId], references: [id]) + partId String + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} + +model SubjectAccessRequest { + id String @id @default(uuid()) + comment String + payload String // json dump + user User @relation(fields: [userId], references: [id]) + userId String + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} diff --git a/api/prisma/migrations/20201227195638-add-subject-access-request-table/steps.json b/api/prisma/migrations/20201227195638-add-subject-access-request-table/steps.json new file mode 100644 index 0000000..d0ee8a8 --- /dev/null +++ b/api/prisma/migrations/20201227195638-add-subject-access-request-table/steps.json @@ -0,0 +1,176 @@ +{ + "version": "0.3.14-fixed", + "steps": [ + { + "tag": "CreateModel", + "model": "SubjectAccessRequest" + }, + { + "tag": "CreateField", + "model": "SubjectAccessRequest", + "field": "id", + "type": "String", + "arity": "Required" + }, + { + "tag": "CreateDirective", + "location": { + "path": { + "tag": "Field", + "model": "SubjectAccessRequest", + "field": "id" + }, + "directive": "id" + } + }, + { + "tag": "CreateDirective", + "location": { + "path": { + "tag": "Field", + "model": "SubjectAccessRequest", + "field": "id" + }, + "directive": "default" + } + }, + { + "tag": "CreateArgument", + "location": { + "tag": "Directive", + "path": { + "tag": "Field", + "model": "SubjectAccessRequest", + "field": "id" + }, + "directive": "default" + }, + "argument": "", + "value": "uuid()" + }, + { + "tag": "CreateField", + "model": "SubjectAccessRequest", + "field": "comment", + "type": "String", + "arity": "Required" + }, + { + "tag": "CreateField", + "model": "SubjectAccessRequest", + "field": "payload", + "type": "String", + "arity": "Required" + }, + { + "tag": "CreateField", + "model": "SubjectAccessRequest", + "field": "user", + "type": "User", + "arity": "Required" + }, + { + "tag": "CreateDirective", + "location": { + "path": { + "tag": "Field", + "model": "SubjectAccessRequest", + "field": "user" + }, + "directive": "relation" + } + }, + { + "tag": "CreateArgument", + "location": { + "tag": "Directive", + "path": { + "tag": "Field", + "model": "SubjectAccessRequest", + "field": "user" + }, + "directive": "relation" + }, + "argument": "fields", + "value": "[userId]" + }, + { + "tag": "CreateArgument", + "location": { + "tag": "Directive", + "path": { + "tag": "Field", + "model": "SubjectAccessRequest", + "field": "user" + }, + "directive": "relation" + }, + "argument": "references", + "value": "[id]" + }, + { + "tag": "CreateField", + "model": "SubjectAccessRequest", + "field": "userId", + "type": "String", + "arity": "Required" + }, + { + "tag": "CreateField", + "model": "SubjectAccessRequest", + "field": "createdAt", + "type": "DateTime", + "arity": "Required" + }, + { + "tag": "CreateDirective", + "location": { + "path": { + "tag": "Field", + "model": "SubjectAccessRequest", + "field": "createdAt" + }, + "directive": "default" + } + }, + { + "tag": "CreateArgument", + "location": { + "tag": "Directive", + "path": { + "tag": "Field", + "model": "SubjectAccessRequest", + "field": "createdAt" + }, + "directive": "default" + }, + "argument": "", + "value": "now()" + }, + { + "tag": "CreateField", + "model": "SubjectAccessRequest", + "field": "updatedAt", + "type": "DateTime", + "arity": "Required" + }, + { + "tag": "CreateDirective", + "location": { + "path": { + "tag": "Field", + "model": "SubjectAccessRequest", + "field": "updatedAt" + }, + "directive": "updatedAt" + } + }, + { + "tag": "CreateField", + "model": "User", + "field": "SubjectAccessRequest", + "type": "SubjectAccessRequest", + "arity": "List" + } + ] +} \ No newline at end of file diff --git a/api/prisma/migrations/migrate.lock b/api/prisma/migrations/migrate.lock index d4f2e8e..2996315 100644 --- a/api/prisma/migrations/migrate.lock +++ b/api/prisma/migrations/migrate.lock @@ -2,4 +2,5 @@ 20201101183848-db-init 20201105184423-add-name-to-user -20201213004819-add-delete-on-part \ No newline at end of file +20201213004819-add-delete-on-part +20201227195638-add-subject-access-request-table \ No newline at end of file diff --git a/api/prisma/schema.prisma b/api/prisma/schema.prisma index 13f644a..8a82a49 100644 --- a/api/prisma/schema.prisma +++ b/api/prisma/schema.prisma @@ -31,11 +31,12 @@ model User { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt - image String? // url maybe id or file storage service? cloudinary? - bio String? //mark down - Part Part[] - Reaction PartReaction[] - Comment Comment[] + image String? // url maybe id or file storage service? cloudinary? + bio String? //mark down + Part Part[] + Reaction PartReaction[] + Comment Comment[] + SubjectAccessRequest SubjectAccessRequest[] } model Part { @@ -79,3 +80,14 @@ model Comment { createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } + +model SubjectAccessRequest { + id String @id @default(uuid()) + comment String + payload String // json dump + user User @relation(fields: [userId], references: [id]) + userId String + + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} diff --git a/api/src/graphql/subjectAccessRequests.sdl.js b/api/src/graphql/subjectAccessRequests.sdl.js new file mode 100644 index 0000000..579b008 --- /dev/null +++ b/api/src/graphql/subjectAccessRequests.sdl.js @@ -0,0 +1,39 @@ +export const schema = gql` + type SubjectAccessRequest { + id: String! + comment: String! + payload: String! + user: User! + userId: String! + createdAt: DateTime! + updatedAt: DateTime! + } + + type Query { + subjectAccessRequests: [SubjectAccessRequest!]! + subjectAccessRequest(id: String!): SubjectAccessRequest + } + + input CreateSubjectAccessRequestInput { + comment: String! + payload: String! + userId: String! + } + + input UpdateSubjectAccessRequestInput { + comment: String + payload: String + userId: String + } + + type Mutation { + createSubjectAccessRequest( + input: CreateSubjectAccessRequestInput! + ): SubjectAccessRequest! + updateSubjectAccessRequest( + id: String! + input: UpdateSubjectAccessRequestInput! + ): SubjectAccessRequest! + deleteSubjectAccessRequest(id: String!): SubjectAccessRequest! + } +` diff --git a/api/src/graphql/users.sdl.js b/api/src/graphql/users.sdl.js index 1830193..5365ba2 100644 --- a/api/src/graphql/users.sdl.js +++ b/api/src/graphql/users.sdl.js @@ -12,6 +12,7 @@ export const schema = gql` Part(partTitle: String): Part Reaction: [PartReaction]! Comment: [Comment]! + SubjectAccessRequest: [SubjectAccessRequest]! } type Query { diff --git a/api/src/services/helpers.js b/api/src/services/helpers.js index 8a488bf..75faa62 100644 --- a/api/src/services/helpers.js +++ b/api/src/services/helpers.js @@ -1,3 +1,10 @@ +import { v2 as cloudinary } from 'cloudinary' +cloudinary.config({ + cloud_name: 'irevdev', + api_key: process.env.CLOUDINARY_API_KEY, + api_secret: process.env.CLOUDINARY_API_SECRET, +}) + export const foreignKeyReplacement = (input) => { let output = input const foreignKeys = Object.keys(input).filter((k) => k.match(/Id$/)) @@ -28,3 +35,14 @@ export const generateUniqueString = async ( const newSeed = count === 1 ? `${seed}_${count}` : seed.slice(0, -1) + count return generateUniqueString(newSeed, isUniqueCallback, count) } + +export const destroyImage = ({ publicId }) => + new Promise((resolve, reject) => { + cloudinary.uploader.destroy(publicId, (error, result) => { + if (error) { + reject(error) + return + } + resolve(result) + }) + }) diff --git a/api/src/services/parts/parts.js b/api/src/services/parts/parts.js index d7b0df1..67385e2 100644 --- a/api/src/services/parts/parts.js +++ b/api/src/services/parts/parts.js @@ -3,6 +3,7 @@ import { foreignKeyReplacement, enforceAlphaNumeric, generateUniqueString, + destroyImage, } from 'src/services/helpers' import { requireAuth } from 'src/lib/auth' import { requireOwnership } from 'src/lib/owner' @@ -74,10 +75,18 @@ export const updatePart = async ({ id, input }) => { if (input.title) { input.title = enforceAlphaNumeric(input.title) } - return db.part.update({ + const originalPart = await db.part.findOne({ where: { id } }) + const imageToDestroy = + originalPart.mainImage !== input.mainImage && originalPart.mainImage + const update = await db.part.update({ data: foreignKeyReplacement(input), where: { id }, }) + if (imageToDestroy) { + // destroy after the db has been updated + destroyImage({ publicId: imageToDestroy }) + } + return update } export const deletePart = async ({ id }) => { diff --git a/api/src/services/subjectAccessRequests/subjectAccessRequests.js b/api/src/services/subjectAccessRequests/subjectAccessRequests.js new file mode 100644 index 0000000..8776520 --- /dev/null +++ b/api/src/services/subjectAccessRequests/subjectAccessRequests.js @@ -0,0 +1,42 @@ +import { db } from 'src/lib/db' +import { requireAuth } from 'src/lib/auth' +import { foreignKeyReplacement } from 'src/services/helpers' + +export const subjectAccessRequests = () => { + requireAuth({ role: 'admin' }) + return db.subjectAccessRequest.findMany() +} + +export const subjectAccessRequest = ({ id }) => { + requireAuth({ role: 'admin' }) + return db.subjectAccessRequest.findOne({ + where: { id }, + }) +} + +export const createSubjectAccessRequest = ({ input }) => { + requireAuth({ role: 'admin' }) + return db.subjectAccessRequest.create({ + data: foreignKeyReplacement(input), + }) +} + +export const updateSubjectAccessRequest = ({ id, input }) => { + requireAuth({ role: 'admin' }) + return db.subjectAccessRequest.update({ + data: foreignKeyReplacement(input), + where: { id }, + }) +} + +export const deleteSubjectAccessRequest = ({ id }) => { + requireAuth({ role: 'admin' }) + return db.subjectAccessRequest.delete({ + where: { id }, + }) +} + +export const SubjectAccessRequest = { + user: (_obj, { root }) => + db.subjectAccessRequest.findOne({ where: { id: root.id } }).user(), +} diff --git a/api/src/services/subjectAccessRequests/subjectAccessRequests.test.js b/api/src/services/subjectAccessRequests/subjectAccessRequests.test.js new file mode 100644 index 0000000..88bcd4c --- /dev/null +++ b/api/src/services/subjectAccessRequests/subjectAccessRequests.test.js @@ -0,0 +1,9 @@ +/* +import { subjectAccessRequests } from './subjectAccessRequests' +*/ + +describe('subjectAccessRequests', () => { + it('returns true', () => { + expect(true).toBe(true) + }) +}) diff --git a/api/src/services/users/users.js b/api/src/services/users/users.js index 7035f5a..2cb0586 100644 --- a/api/src/services/users/users.js +++ b/api/src/services/users/users.js @@ -2,7 +2,7 @@ import { db } from 'src/lib/db' import { requireAuth } from 'src/lib/auth' import { requireOwnership } from 'src/lib/owner' import { UserInputError } from '@redwoodjs/api' -import { enforceAlphaNumeric } from 'src/services/helpers' +import { enforceAlphaNumeric, destroyImage } from 'src/services/helpers' export const users = () => { requireAuth({ role: 'admin' }) @@ -51,10 +51,18 @@ export const updateUserByUserName = async ({ userName, input }) => { `You've tried to used a protected word as you userName, try something other than ` ) } - return db.user.update({ + const originalPart = await db.user.findOne({ where: { userName } }) + const imageToDestroy = + originalPart.image !== input.image && originalPart.image + const update = await db.user.update({ data: input, where: { userName }, }) + if (imageToDestroy) { + // destroy after the db has been updated + destroyImage({ publicId: imageToDestroy }) + } + return update } export const deleteUser = ({ id }) => { @@ -80,4 +88,6 @@ export const User = { db.user.findOne({ where: { id: root.id } }).Reaction(), Comment: (_obj, { root }) => db.user.findOne({ where: { id: root.id } }).Comment(), + SubjectAccessRequest: (_obj, { root }) => + db.user.findOne({ where: { id: root.id } }).SubjectAccessRequest(), } diff --git a/redwood.toml b/redwood.toml index 40dc34e..cd93c42 100644 --- a/redwood.toml +++ b/redwood.toml @@ -8,7 +8,7 @@ [web] port = 8910 apiProxyPath = "/.netlify/functions" - includeEnvironmentVariables = ['GOOGLE_ANALYTICS_ID'] + includeEnvironmentVariables = ['GOOGLE_ANALYTICS_ID', 'CLOUDINARY_API_KEY', 'CLOUDINARY_API_SECRET'] experimentalFastRefresh = true [api] port = 8911 diff --git a/web/src/Routes.js b/web/src/Routes.js index e15d04d..eeadbf6 100644 --- a/web/src/Routes.js +++ b/web/src/Routes.js @@ -32,6 +32,7 @@ const Routes = () => { ) return ( + @@ -53,6 +54,9 @@ const Routes = () => { + + + ) diff --git a/web/src/components/Breadcrumb/Breadcrumb.js b/web/src/components/Breadcrumb/Breadcrumb.js index 1057db4..796e00d 100644 --- a/web/src/components/Breadcrumb/Breadcrumb.js +++ b/web/src/components/Breadcrumb/Breadcrumb.js @@ -2,7 +2,13 @@ import { getActiveClasses } from 'get-active-classes' import InputText from 'src/components/InputText' -const Breadcrumb = ({ userName, partTitle, onPartTitleChange, className }) => { +const Breadcrumb = ({ + userName, + partTitle, + onPartTitleChange, + className, + isInvalid, +}) => { return (

@@ -26,6 +32,7 @@ const Breadcrumb = ({ userName, partTitle, onPartTitleChange, className }) => { className={getActiveClasses('text-indigo-800 text-2xl', { '-ml-2': !onPartTitleChange, })} + isInvalid={isInvalid} />

) diff --git a/web/src/components/EditSubjectAccessRequestCell/EditSubjectAccessRequestCell.js b/web/src/components/EditSubjectAccessRequestCell/EditSubjectAccessRequestCell.js new file mode 100644 index 0000000..77c4d13 --- /dev/null +++ b/web/src/components/EditSubjectAccessRequestCell/EditSubjectAccessRequestCell.js @@ -0,0 +1,70 @@ +import { useMutation, useFlash } from '@redwoodjs/web' +import { navigate, routes } from '@redwoodjs/router' +import SubjectAccessRequestForm from 'src/components/SubjectAccessRequestForm' + +export const QUERY = gql` + query FIND_SUBJECT_ACCESS_REQUEST_BY_ID($id: String!) { + subjectAccessRequest: subjectAccessRequest(id: $id) { + id + comment + payload + userId + createdAt + updatedAt + } + } +` +const UPDATE_SUBJECT_ACCESS_REQUEST_MUTATION = gql` + mutation UpdateSubjectAccessRequestMutation( + $id: String! + $input: UpdateSubjectAccessRequestInput! + ) { + updateSubjectAccessRequest(id: $id, input: $input) { + id + comment + payload + userId + createdAt + updatedAt + } + } +` + +export const Loading = () =>
Loading...
+ +export const Success = ({ subjectAccessRequest }) => { + const { addMessage } = useFlash() + const [updateSubjectAccessRequest, { loading, error }] = useMutation( + UPDATE_SUBJECT_ACCESS_REQUEST_MUTATION, + { + onCompleted: () => { + navigate(routes.subjectAccessRequests()) + addMessage('SubjectAccessRequest updated.', { + classes: 'rw-flash-success', + }) + }, + } + ) + + const onSave = (input, id) => { + updateSubjectAccessRequest({ variables: { id, input } }) + } + + return ( +
+
+

+ Edit SubjectAccessRequest {subjectAccessRequest.id} +

+
+
+ +
+
+ ) +} diff --git a/web/src/components/EditUserCell/EditUserCell.js b/web/src/components/EditUserCell/EditUserCell.js index 456c83f..7f4914d 100644 --- a/web/src/components/EditUserCell/EditUserCell.js +++ b/web/src/components/EditUserCell/EditUserCell.js @@ -32,7 +32,7 @@ export const Empty = () =>
Empty
export const Failure = ({ error }) =>
Error: {error.message}
-export const Success = ({ user }) => { +export const Success = ({ user, variables: { isEditable } }) => { const { addMessage } = useFlash() const [updateUser, { loading, error }] = useMutation(UPDATE_USER_MUTATION, { onCompleted: ({ updateUserByUserName }) => { @@ -51,7 +51,7 @@ export const Success = ({ user }) => { onSave={onSave} loading={loading} error={error} - isEditable + isEditable={isEditable} /> ) } diff --git a/web/src/components/Footer/Footer.js b/web/src/components/Footer/Footer.js new file mode 100644 index 0000000..b4e1bd1 --- /dev/null +++ b/web/src/components/Footer/Footer.js @@ -0,0 +1,16 @@ +import { Link, routes } from '@redwoodjs/router' + +const Footer = () => { + return ( +
+
+ + Code of Conduct + + Privacy Policy +
+
+ ) +} + +export default Footer diff --git a/web/src/components/Footer/Footer.stories.js b/web/src/components/Footer/Footer.stories.js new file mode 100644 index 0000000..f53294a --- /dev/null +++ b/web/src/components/Footer/Footer.stories.js @@ -0,0 +1,7 @@ +import Footer from './Footer' + +export const generated = () => { + return