Add Privacy Policy related improvements
various thing to make sure we're GDPR, et al compliant
This commit is contained in:
@@ -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 (
|
||||
<h3 className={getActiveClasses('text-2xl font-roboto', className)}>
|
||||
<div className="w-1 inline-block text-indigo-800 bg-indigo-800 mr-2">
|
||||
@@ -26,6 +32,7 @@ const Breadcrumb = ({ userName, partTitle, onPartTitleChange, className }) => {
|
||||
className={getActiveClasses('text-indigo-800 text-2xl', {
|
||||
'-ml-2': !onPartTitleChange,
|
||||
})}
|
||||
isInvalid={isInvalid}
|
||||
/>
|
||||
</h3>
|
||||
)
|
||||
|
||||
@@ -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 = () => <div>Loading...</div>
|
||||
|
||||
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 (
|
||||
<div className="rw-segment">
|
||||
<header className="rw-segment-header">
|
||||
<h2 className="rw-heading rw-heading-secondary">
|
||||
Edit SubjectAccessRequest {subjectAccessRequest.id}
|
||||
</h2>
|
||||
</header>
|
||||
<div className="rw-segment-main">
|
||||
<SubjectAccessRequestForm
|
||||
subjectAccessRequest={subjectAccessRequest}
|
||||
onSave={onSave}
|
||||
error={error}
|
||||
loading={loading}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -32,7 +32,7 @@ export const Empty = () => <div>Empty</div>
|
||||
|
||||
export const Failure = ({ error }) => <div>Error: {error.message}</div>
|
||||
|
||||
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}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
16
web/src/components/Footer/Footer.js
Normal file
16
web/src/components/Footer/Footer.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Link, routes } from '@redwoodjs/router'
|
||||
|
||||
const Footer = () => {
|
||||
return (
|
||||
<div className="bg-indigo-900 text-indigo-200 font-roboto mt-20 text-sm">
|
||||
<div className="flex h-16 justify-end items-center mx-16">
|
||||
<Link className="mr-8" to={routes.codeOfConduct()}>
|
||||
Code of Conduct
|
||||
</Link>
|
||||
<Link to={routes.privacyPolicy()}>Privacy Policy</Link>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Footer
|
||||
7
web/src/components/Footer/Footer.stories.js
Normal file
7
web/src/components/Footer/Footer.stories.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import Footer from './Footer'
|
||||
|
||||
export const generated = () => {
|
||||
return <Footer />
|
||||
}
|
||||
|
||||
export default { title: 'Components/Footer' }
|
||||
11
web/src/components/Footer/Footer.test.js
Normal file
11
web/src/components/Footer/Footer.test.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import { render } from '@redwoodjs/testing'
|
||||
|
||||
import Footer from './Footer'
|
||||
|
||||
describe('Footer', () => {
|
||||
it('renders successfully', () => {
|
||||
expect(() => {
|
||||
render(<Footer />)
|
||||
}).not.toThrow()
|
||||
})
|
||||
})
|
||||
@@ -59,7 +59,6 @@ const IdeToolbar = ({ canEdit, isChanges, onSave, onExport, userNamePart }) => {
|
||||
<div className="h-8 w-8 ml-4">
|
||||
<ImageUploader
|
||||
className="rounded-full object-cover"
|
||||
onImageUpload={() => {}}
|
||||
aspectRatio={1}
|
||||
imageUrl={userNamePart?.image}
|
||||
width={80}
|
||||
|
||||
@@ -12,7 +12,7 @@ const CLOUDINARY_UPLOAD_PRESET = 'CadHub_project_images'
|
||||
const CLOUDINARY_UPLOAD_URL = 'https://api.cloudinary.com/v1_1/irevdev/upload'
|
||||
|
||||
export default function ImageUploader({
|
||||
onImageUpload,
|
||||
onImageUpload = () => {},
|
||||
imageUrl,
|
||||
aspectRatio,
|
||||
className,
|
||||
@@ -67,11 +67,12 @@ export default function ImageUploader({
|
||||
>
|
||||
<div className="absolute w-full h-full" {...getRootProps()}>
|
||||
{cloudinaryId && isEditable && (
|
||||
<button className="absolute z-10 w-full inset-0 bg-indigo-900 opacity-50 flex justify-center items-center">
|
||||
<button className="absolute z-10 bg-indigo-900 opacity-75 bottom-0 right-0 flex items-center p-1 mb-6 mr-2 rounded-lg">
|
||||
<span className="text-gray-100 pr-2">Update</span>
|
||||
<Svg
|
||||
name="pencil"
|
||||
strokeWidth={2}
|
||||
className="text-gray-300 h-24 w-24"
|
||||
className=" text-gray-100 h-6 w-6"
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import { getActiveClasses } from 'get-active-classes'
|
||||
|
||||
const InputText = ({ value, isEditable, onChange, className }) => {
|
||||
const InputText = ({
|
||||
value,
|
||||
isEditable,
|
||||
onChange,
|
||||
className,
|
||||
isInvalid = false,
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
@@ -10,7 +16,12 @@ const InputText = ({ value, isEditable, onChange, className }) => {
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div className="absolute inset-0 mb-2 rounded bg-gray-200 shadow-inner" />
|
||||
<div
|
||||
className={getActiveClasses(
|
||||
'absolute inset-0 mb-2 rounded bg-gray-200 shadow-inner',
|
||||
{ 'border border-red-500': isInvalid }
|
||||
)}
|
||||
/>
|
||||
<input
|
||||
className="pl-2 pt-1 text-indigo-800 font-medium mb-px pb-px bg-transparent relative"
|
||||
onChange={onChange}
|
||||
|
||||
@@ -191,6 +191,14 @@ const SignUpForm = ({ onSubmitSignUp, checkBox, setCheckBox, onClose }) => (
|
||||
>
|
||||
Code of Conduct
|
||||
</Link>
|
||||
, and agree with our{' '}
|
||||
<Link
|
||||
onClick={onClose}
|
||||
to={routes.privacyPolicy()}
|
||||
className="underline"
|
||||
>
|
||||
Privacy Policy
|
||||
</Link>
|
||||
</span>
|
||||
</div>
|
||||
<HeroButton text="Sign Up" />
|
||||
|
||||
@@ -21,8 +21,10 @@ const PartProfile = ({
|
||||
}) => {
|
||||
const [comment, setComment] = useState('')
|
||||
const [isConfirmDialogOpen, setIsConfirmDialogOpen] = useState(false)
|
||||
const [isInvalid, setIsInvalid] = useState(false)
|
||||
const { currentUser } = useAuth()
|
||||
const canEdit = currentUser?.sub === userPart.id
|
||||
const isImageEditable = !isEditable && canEdit // image is editable when not in profile edit mode in order to separate them as it's too hard too to upload an image to cloudinary temporarily until the use saves (and maybe have to clean up) for the time being
|
||||
const part = userPart?.Part
|
||||
const emotes = countEmotes(part?.Reaction)
|
||||
const userEmotes = part?.userReactions.map(({ emote }) => emote)
|
||||
@@ -48,11 +50,19 @@ const PartProfile = ({
|
||||
setProperty('title', target.value.replace(/([^a-zA-Z\d_:])/g, '-'))
|
||||
const onDescriptionChange = (description) =>
|
||||
setProperty('description', description())
|
||||
const onImageUpload = ({ cloudinaryPublicId }) =>
|
||||
setProperty('mainImage', cloudinaryPublicId)
|
||||
const onEditSaveClick = () => {
|
||||
const onImageUpload = ({ cloudinaryPublicId }) => {
|
||||
onSave(part?.id, { ...input, mainImage: cloudinaryPublicId })
|
||||
}
|
||||
// setProperty('mainImage', cloudinaryPublicId)
|
||||
const onEditSaveClick = (hi) => {
|
||||
// do a thing
|
||||
if (isEditable) {
|
||||
input.title && onSave(part?.id, input)
|
||||
if (!input.title) {
|
||||
setIsInvalid(true)
|
||||
return
|
||||
}
|
||||
setIsInvalid(false)
|
||||
onSave(part?.id, input)
|
||||
return
|
||||
}
|
||||
navigate(
|
||||
@@ -69,7 +79,6 @@ const PartProfile = ({
|
||||
<aside className="col-start-2 relative">
|
||||
<ImageUploader
|
||||
className="rounded-half rounded-br-lg shadow-md border-2 border-gray-200 border-solid"
|
||||
onImageUpload={() => {}}
|
||||
aspectRatio={1}
|
||||
imageUrl={userPart?.image}
|
||||
width={300}
|
||||
@@ -144,13 +153,14 @@ const PartProfile = ({
|
||||
onPartTitleChange={isEditable && onTitleChange}
|
||||
userName={userPart?.userName}
|
||||
partTitle={input?.title}
|
||||
isInvalid={isInvalid}
|
||||
/>
|
||||
{!!(input?.mainImage || isEditable) && (
|
||||
{!!input?.mainImage && !isEditable && part?.id && (
|
||||
<ImageUploader
|
||||
className="rounded-lg shadow-md border-2 border-gray-200 border-solid mt-8"
|
||||
onImageUpload={onImageUpload}
|
||||
aspectRatio={16 / 9}
|
||||
isEditable={isEditable}
|
||||
isEditable={isImageEditable}
|
||||
imageUrl={input?.mainImage}
|
||||
width={1010}
|
||||
/>
|
||||
@@ -183,7 +193,6 @@ const PartProfile = ({
|
||||
<div className="w-8 h-8 overflow-hidden rounded-full border border-indigo-300 shadow flex-shrink-0">
|
||||
<ImageUploader
|
||||
className=""
|
||||
onImageUpload={() => {}}
|
||||
aspectRatio={1}
|
||||
imageUrl={user?.image}
|
||||
width={50}
|
||||
|
||||
@@ -36,7 +36,6 @@ const PartsList = ({ parts, shouldFilterPartsWithoutImage = false }) => {
|
||||
<div className="w-8 h-8 overflow-hidden rounded-full border border-indigo-300 shadow">
|
||||
<ImageUploader
|
||||
className=""
|
||||
onImageUpload={() => {}}
|
||||
aspectRatio={1}
|
||||
imageUrl={user?.image}
|
||||
width={50}
|
||||
@@ -49,7 +48,6 @@ const PartsList = ({ parts, shouldFilterPartsWithoutImage = false }) => {
|
||||
<div className="w-full overflow-hidden relative rounded-b-lg">
|
||||
<ImageUploader
|
||||
className=""
|
||||
onImageUpload={() => {}}
|
||||
aspectRatio={1.4}
|
||||
imageUrl={mainImage}
|
||||
width={700}
|
||||
|
||||
119
web/src/components/SubjectAccessRequest/SubjectAccessRequest.js
Normal file
119
web/src/components/SubjectAccessRequest/SubjectAccessRequest.js
Normal file
@@ -0,0 +1,119 @@
|
||||
import { useMutation, useFlash } from '@redwoodjs/web'
|
||||
import { Link, routes, navigate } from '@redwoodjs/router'
|
||||
|
||||
import { QUERY } from 'src/components/SubjectAccessRequestsCell'
|
||||
|
||||
const DELETE_SUBJECT_ACCESS_REQUEST_MUTATION = gql`
|
||||
mutation DeleteSubjectAccessRequestMutation($id: String!) {
|
||||
deleteSubjectAccessRequest(id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const jsonDisplay = (obj) => {
|
||||
return (
|
||||
<pre>
|
||||
<code>{JSON.stringify(obj, null, 2)}</code>
|
||||
</pre>
|
||||
)
|
||||
}
|
||||
|
||||
const timeTag = (datetime) => {
|
||||
return (
|
||||
<time dateTime={datetime} title={datetime}>
|
||||
{new Date(datetime).toUTCString()}
|
||||
</time>
|
||||
)
|
||||
}
|
||||
|
||||
const checkboxInputTag = (checked) => {
|
||||
return <input type="checkbox" checked={checked} disabled />
|
||||
}
|
||||
|
||||
const SubjectAccessRequest = ({ subjectAccessRequest }) => {
|
||||
const { addMessage } = useFlash()
|
||||
const [deleteSubjectAccessRequest] = useMutation(
|
||||
DELETE_SUBJECT_ACCESS_REQUEST_MUTATION,
|
||||
{
|
||||
onCompleted: () => {
|
||||
navigate(routes.subjectAccessRequests())
|
||||
addMessage('SubjectAccessRequest deleted.', {
|
||||
classes: 'rw-flash-success',
|
||||
})
|
||||
},
|
||||
// This refetches the query on the list page. Read more about other ways to
|
||||
// update the cache over here:
|
||||
// https://www.apollographql.com/docs/react/data/mutations/#making-all-other-cache-updates
|
||||
refetchQueries: [{ query: QUERY }],
|
||||
awaitRefetchQueries: true,
|
||||
}
|
||||
)
|
||||
|
||||
const onDeleteClick = (id) => {
|
||||
if (
|
||||
confirm(
|
||||
'Are you sure you want to delete subjectAccessRequest ' + id + '?'
|
||||
)
|
||||
) {
|
||||
deleteSubjectAccessRequest({ variables: { id } })
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="rw-segment">
|
||||
<header className="rw-segment-header">
|
||||
<h2 className="rw-heading rw-heading-secondary">
|
||||
SubjectAccessRequest {subjectAccessRequest.id} Detail
|
||||
</h2>
|
||||
</header>
|
||||
<table className="rw-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<td>{subjectAccessRequest.id}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Comment</th>
|
||||
<td>{subjectAccessRequest.comment}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Payload</th>
|
||||
<td>{subjectAccessRequest.payload}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>User id</th>
|
||||
<td>{subjectAccessRequest.userId}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Created at</th>
|
||||
<td>{timeTag(subjectAccessRequest.createdAt)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Updated at</th>
|
||||
<td>{timeTag(subjectAccessRequest.updatedAt)}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<nav className="rw-button-group">
|
||||
<Link
|
||||
to={routes.editSubjectAccessRequest({ id: subjectAccessRequest.id })}
|
||||
className="rw-button rw-button-blue"
|
||||
>
|
||||
Edit
|
||||
</Link>
|
||||
<a
|
||||
href="#"
|
||||
className="rw-button rw-button-red"
|
||||
onClick={() => onDeleteClick(subjectAccessRequest.id)}
|
||||
>
|
||||
Delete
|
||||
</a>
|
||||
</nav>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default SubjectAccessRequest
|
||||
@@ -0,0 +1,22 @@
|
||||
import SubjectAccessRequest from 'src/components/SubjectAccessRequest'
|
||||
|
||||
export const QUERY = gql`
|
||||
query FIND_SUBJECT_ACCESS_REQUEST_BY_ID($id: String!) {
|
||||
subjectAccessRequest: subjectAccessRequest(id: $id) {
|
||||
id
|
||||
comment
|
||||
payload
|
||||
userId
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const Loading = () => <div>Loading...</div>
|
||||
|
||||
export const Empty = () => <div>SubjectAccessRequest not found</div>
|
||||
|
||||
export const Success = ({ subjectAccessRequest }) => {
|
||||
return <SubjectAccessRequest subjectAccessRequest={subjectAccessRequest} />
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
import {
|
||||
Form,
|
||||
FormError,
|
||||
FieldError,
|
||||
Label,
|
||||
TextField,
|
||||
Submit,
|
||||
} from '@redwoodjs/forms'
|
||||
|
||||
const SubjectAccessRequestForm = (props) => {
|
||||
const onSubmit = (data) => {
|
||||
props.onSave(data, props?.subjectAccessRequest?.id)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rw-form-wrapper">
|
||||
<Form onSubmit={onSubmit} error={props.error}>
|
||||
<FormError
|
||||
error={props.error}
|
||||
wrapperClassName="rw-form-error-wrapper"
|
||||
titleClassName="rw-form-error-title"
|
||||
listClassName="rw-form-error-list"
|
||||
/>
|
||||
|
||||
<Label
|
||||
name="comment"
|
||||
className="rw-label"
|
||||
errorClassName="rw-label rw-label-error"
|
||||
>
|
||||
Comment
|
||||
</Label>
|
||||
<TextField
|
||||
name="comment"
|
||||
defaultValue={props.subjectAccessRequest?.comment}
|
||||
className="rw-input"
|
||||
errorClassName="rw-input rw-input-error"
|
||||
validation={{ required: true }}
|
||||
/>
|
||||
<FieldError name="comment" className="rw-field-error" />
|
||||
|
||||
<Label
|
||||
name="payload"
|
||||
className="rw-label"
|
||||
errorClassName="rw-label rw-label-error"
|
||||
>
|
||||
Payload
|
||||
</Label>
|
||||
<TextField
|
||||
name="payload"
|
||||
defaultValue={props.subjectAccessRequest?.payload}
|
||||
className="rw-input"
|
||||
errorClassName="rw-input rw-input-error"
|
||||
validation={{ required: true }}
|
||||
/>
|
||||
<FieldError name="payload" className="rw-field-error" />
|
||||
|
||||
<Label
|
||||
name="userId"
|
||||
className="rw-label"
|
||||
errorClassName="rw-label rw-label-error"
|
||||
>
|
||||
User id
|
||||
</Label>
|
||||
<TextField
|
||||
name="userId"
|
||||
defaultValue={props.subjectAccessRequest?.userId}
|
||||
className="rw-input"
|
||||
errorClassName="rw-input rw-input-error"
|
||||
validation={{ required: true }}
|
||||
/>
|
||||
<FieldError name="userId" className="rw-field-error" />
|
||||
|
||||
<div className="rw-button-group">
|
||||
<Submit disabled={props.loading} className="rw-button rw-button-blue">
|
||||
Save
|
||||
</Submit>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SubjectAccessRequestForm
|
||||
@@ -0,0 +1,137 @@
|
||||
import { useMutation, useFlash } from '@redwoodjs/web'
|
||||
import { Link, routes } from '@redwoodjs/router'
|
||||
|
||||
import { QUERY } from 'src/components/SubjectAccessRequestsCell'
|
||||
|
||||
const DELETE_SUBJECT_ACCESS_REQUEST_MUTATION = gql`
|
||||
mutation DeleteSubjectAccessRequestMutation($id: String!) {
|
||||
deleteSubjectAccessRequest(id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const MAX_STRING_LENGTH = 150
|
||||
|
||||
const truncate = (text) => {
|
||||
let output = text
|
||||
if (text && text.length > MAX_STRING_LENGTH) {
|
||||
output = output.substring(0, MAX_STRING_LENGTH) + '...'
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
const jsonTruncate = (obj) => {
|
||||
return truncate(JSON.stringify(obj, null, 2))
|
||||
}
|
||||
|
||||
const timeTag = (datetime) => {
|
||||
return (
|
||||
<time dateTime={datetime} title={datetime}>
|
||||
{new Date(datetime).toUTCString()}
|
||||
</time>
|
||||
)
|
||||
}
|
||||
|
||||
const checkboxInputTag = (checked) => {
|
||||
return <input type="checkbox" checked={checked} disabled />
|
||||
}
|
||||
|
||||
const SubjectAccessRequestsList = ({ subjectAccessRequests }) => {
|
||||
const { addMessage } = useFlash()
|
||||
const [deleteSubjectAccessRequest] = useMutation(
|
||||
DELETE_SUBJECT_ACCESS_REQUEST_MUTATION,
|
||||
{
|
||||
onCompleted: () => {
|
||||
addMessage('SubjectAccessRequest deleted.', {
|
||||
classes: 'rw-flash-success',
|
||||
})
|
||||
},
|
||||
// This refetches the query on the list page. Read more about other ways to
|
||||
// update the cache over here:
|
||||
// https://www.apollographql.com/docs/react/data/mutations/#making-all-other-cache-updates
|
||||
refetchQueries: [{ query: QUERY }],
|
||||
awaitRefetchQueries: true,
|
||||
}
|
||||
)
|
||||
|
||||
const onDeleteClick = (id) => {
|
||||
if (
|
||||
confirm(
|
||||
'Are you sure you want to delete subjectAccessRequest ' + id + '?'
|
||||
)
|
||||
) {
|
||||
deleteSubjectAccessRequest({ variables: { id } })
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="rw-segment rw-table-wrapper-responsive">
|
||||
<table className="rw-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<th>Comment</th>
|
||||
<th>Payload</th>
|
||||
<th>User id</th>
|
||||
<th>Created at</th>
|
||||
<th>Updated at</th>
|
||||
<th> </th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{subjectAccessRequests.map((subjectAccessRequest) => (
|
||||
<tr key={subjectAccessRequest.id}>
|
||||
<td>{truncate(subjectAccessRequest.id)}</td>
|
||||
<td>{truncate(subjectAccessRequest.comment)}</td>
|
||||
<td>{truncate(subjectAccessRequest.payload)}</td>
|
||||
<td>{truncate(subjectAccessRequest.userId)}</td>
|
||||
<td>{timeTag(subjectAccessRequest.createdAt)}</td>
|
||||
<td>{timeTag(subjectAccessRequest.updatedAt)}</td>
|
||||
<td>
|
||||
<nav className="rw-table-actions">
|
||||
<Link
|
||||
to={routes.subjectAccessRequest({
|
||||
id: subjectAccessRequest.id,
|
||||
})}
|
||||
title={
|
||||
'Show subjectAccessRequest ' +
|
||||
subjectAccessRequest.id +
|
||||
' detail'
|
||||
}
|
||||
className="rw-button rw-button-small"
|
||||
>
|
||||
Show
|
||||
</Link>
|
||||
<Link
|
||||
to={routes.editSubjectAccessRequest({
|
||||
id: subjectAccessRequest.id,
|
||||
})}
|
||||
title={
|
||||
'Edit subjectAccessRequest ' + subjectAccessRequest.id
|
||||
}
|
||||
className="rw-button rw-button-small rw-button-blue"
|
||||
>
|
||||
Edit
|
||||
</Link>
|
||||
<a
|
||||
href="#"
|
||||
title={
|
||||
'Delete subjectAccessRequest ' + subjectAccessRequest.id
|
||||
}
|
||||
className="rw-button rw-button-small rw-button-red"
|
||||
onClick={() => onDeleteClick(subjectAccessRequest.id)}
|
||||
>
|
||||
Delete
|
||||
</a>
|
||||
</nav>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SubjectAccessRequestsList
|
||||
@@ -0,0 +1,24 @@
|
||||
import SubjectAccessRequests from 'src/components/SubjectAccessRequests'
|
||||
|
||||
export const QUERY = gql`
|
||||
query SUBJECT_ACCESS_REQUESTS {
|
||||
subjectAccessRequests {
|
||||
id
|
||||
comment
|
||||
payload
|
||||
userId
|
||||
createdAt
|
||||
updatedAt
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const Loading = () => <div>Loading...</div>
|
||||
|
||||
export const Empty = () => {
|
||||
return <div className="rw-text-center">No subjectAccessRequests yet.</div>
|
||||
}
|
||||
|
||||
export const Success = ({ subjectAccessRequests }) => {
|
||||
return <SubjectAccessRequests subjectAccessRequests={subjectAccessRequests} />
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
import UserProfile from 'src/components/UserProfile'
|
||||
|
||||
export const QUERY = gql`
|
||||
query FIND_USER_BY_ID($userName: String!) {
|
||||
user: userName(userName: $userName) {
|
||||
id
|
||||
userName
|
||||
name
|
||||
createdAt
|
||||
updatedAt
|
||||
image
|
||||
bio
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
export const Loading = () => <div>Loading...</div>
|
||||
|
||||
export const Empty = () => <div>User not found</div>
|
||||
|
||||
export const Success = ({ user }) => {
|
||||
return <UserProfile user={user} />
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import PartsOfUser from 'src/components/PartsOfUserCell'
|
||||
const UserProfile = ({ user, isEditable, loading, onSave, error, parts }) => {
|
||||
const { currentUser } = useAuth()
|
||||
const canEdit = currentUser?.sub === user.id
|
||||
const isImageEditable = !isEditable && canEdit // image is editable when not in profile edit mode in order to separate them as it's too hard too to upload an image to cloudinary temporarily until the use saves (and maybe have to clean up) for the time being
|
||||
useEffect(() => {
|
||||
isEditable && !canEdit && navigate(routes.user({ userName: user.userName }))
|
||||
}, [currentUser])
|
||||
@@ -25,21 +26,23 @@ const UserProfile = ({ user, isEditable, loading, onSave, error, parts }) => {
|
||||
<>
|
||||
<section className="max-w-2xl mx-auto mt-20 ">
|
||||
<div className="flex">
|
||||
<div className="w-40 flex-shrink-0">
|
||||
<ImageUploader
|
||||
className="rounded-half rounded-br-lg shadow-md border-2 border-gray-200 border-solid"
|
||||
onImageUpload={({ cloudinaryPublicId: image }) =>
|
||||
setInput({
|
||||
...input,
|
||||
image,
|
||||
})
|
||||
}
|
||||
aspectRatio={1}
|
||||
isEditable={isEditable}
|
||||
imageUrl={user.image}
|
||||
width={300}
|
||||
/>
|
||||
</div>
|
||||
{!isEditable && (
|
||||
<div className="w-40 flex-shrink-0">
|
||||
<ImageUploader
|
||||
className="rounded-half rounded-br-lg shadow-md border-2 border-gray-200 border-solid"
|
||||
onImageUpload={({ cloudinaryPublicId: image }) => {
|
||||
onSave(user.userName, {
|
||||
...input,
|
||||
image,
|
||||
})
|
||||
}}
|
||||
aspectRatio={1}
|
||||
isEditable={isImageEditable}
|
||||
imageUrl={user.image}
|
||||
width={300}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="ml-6 flex flex-col justify-between">
|
||||
<ProfileTextInput
|
||||
fields={editableTextFields}
|
||||
|
||||
Reference in New Issue
Block a user