Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

74 notes feature for photoinput and photo components #253

Merged
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
d26fad9
74 : Added text input to the PhotoInput. Adjusted some parameter desc…
lindsayJohnstonPnnl Dec 17, 2024
65b928d
74: Got one comment to come through.
lindsayJohnstonPnnl Dec 17, 2024
1e66043
74: Messy, need to clean up logic, but noteValue is saving to store a…
lindsayJohnstonPnnl Dec 18, 2024
a157e96
74: Moved the TextInput below the Add Photo button.
lindsayJohnstonPnnl Dec 18, 2024
0a57f8b
74 : Refactored and cleaned up code.
lindsayJohnstonPnnl Dec 18, 2024
293b07b
74 : Cleaned up code, removed debuggers.
lindsayJohnstonPnnl Dec 18, 2024
302fa3a
74: Added note field to the <Photo/> component.
lindsayJohnstonPnnl Dec 18, 2024
dd64e5c
74 : Added conditional logic so that we can turn notes section on/off…
lindsayJohnstonPnnl Dec 18, 2024
fc40407
74 : Remove notes section for building number photo prompt.
lindsayJohnstonPnnl Dec 18, 2024
6ebaf84
74: Replaced TextInput with TextInputWrapper.
lindsayJohnstonPnnl Jan 8, 2025
469e95b
74: Merge conflict resolution.
lindsayJohnstonPnnl Jan 8, 2025
979e0c3
74: Cleaned up code.
lindsayJohnstonPnnl Jan 8, 2025
dbca760
74 :Deleted the JSON gymnastics I was doing and replaced with the get…
lindsayJohnstonPnnl Jan 8, 2025
157c5fc
Merge remote-tracking branch 'origin/main' into 74-notes-feature-for-…
lindsayJohnstonPnnl Jan 17, 2025
9d1a62a
74: Notes with multiple lines now show up on multiple lines in the re…
lindsayJohnstonPnnl Jan 17, 2025
f5e392e
74: Made the textarea get taller on user click.
lindsayJohnstonPnnl Jan 17, 2025
659ea54
74 : First stab at adding classes that get the textarea and label to …
lindsayJohnstonPnnl Jan 20, 2025
ab128d0
74 : Cleaned up code.
lindsayJohnstonPnnl Jan 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
504 changes: 252 additions & 252 deletions src/App.css

Large diffs are not rendered by default.

18 changes: 17 additions & 1 deletion src/components/photo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import { Card, Image, Row, Col } from 'react-bootstrap'
import DateTimeStr from './date_time_str'
import GpsCoordStr from './gps_coord_str'
import type PhotoMetadata from '../types/photo_metadata.type'
import JSONValue from '../types/json_value.type'

interface PhotoProps {
description: React.ReactNode
label: string
photos: { id: string; photo: Blob; metadata: PhotoMetadata }[] // Array of photo objects with metadata
required: boolean
noteValue: JSONValue
}

/**
Expand All @@ -30,13 +32,27 @@ interface PhotoProps {
* @param required When unset, the Photo component will only show if there is a
* photo attachment in the data store with the given id. When set, the Photo component
* will always show and the Photo component will indicate when the photo is missing.
* @param noteValue The value to be displayed as the note for the photos
*/
const Photo: FC<PhotoProps> = ({ description, label, photos, required }) => {
const Photo: FC<PhotoProps> = ({
description,
label,
photos,
required,
noteValue,
}) => {
return (photos && photos.length > 0) || required ? (
<Card className="photo-card">
<Card.Body>
<Card.Title>{label}</Card.Title>
<Card.Text as="div">{description}</Card.Text>
{noteValue && (
<div className="photo-notes">
<h3>Notes: </h3>
<div>{noteValue ? noteValue.toString() : null}</div>
</div>
)}

{photos && photos.length > 0
? Array.isArray(photos) && (
<Row className="photo-row">
Expand Down
29 changes: 27 additions & 2 deletions src/components/photo_input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import GpsCoordStr from './gps_coord_str'
import type PhotoMetaData from '../types/photo_metadata.type'
import { PHOTO_MIME_TYPES } from '../utilities/photo_utils'
import { TfiTrash } from 'react-icons/tfi'
import TextInput from './text_input'
lindsayJohnstonPnnl marked this conversation as resolved.
Show resolved Hide resolved
import JSONValue from '../types/json_value.type'

interface PhotoInputProps {
children: React.ReactNode
Expand All @@ -21,6 +23,10 @@ interface PhotoInputProps {
loading: boolean
error: string
count: number
noteValue: JSONValue | undefined
lindsayJohnstonPnnl marked this conversation as resolved.
Show resolved Hide resolved
updateNoteValue: (value: string) => void
id: string
notes?: boolean
}
// TODO: Determine whether or not the useEffect() method is needed.
// We don't seem to need a separate camera button on an Android phone.
Expand All @@ -39,18 +45,25 @@ interface PhotoInputProps {
* @param uploadable When set, the PhotoInput component will open the gallery to upload the photo.
* When unset, the PhotoInput component will use device camera for taking new photo (default).
* @param loader When set, a loading image will be displayed during the upload process.
* @param noteValue The value to populate the photo caption box
* @param updateNoteValue Function for setting the note in the photo caption box
* @param id Attachment id
* @param notes If notes is false, then the note input will not show up
*/
const PhotoInput: FC<PhotoInputProps> = ({
children,
label,
metadata,
photos,
upsertPhoto,
deletePhoto,
uploadable,
loading,
error,
count,
noteValue,
updateNoteValue,
id,
notes = true,
}) => {
// Create references to the hidden file inputs
const hiddenPhotoCaptureInputRef = useRef<HTMLInputElement>(null)
Expand Down Expand Up @@ -231,7 +244,7 @@ const PhotoInput: FC<PhotoInputProps> = ({
)}
{error && <div className="error">{error}</div>}
{photos?.length < count && (
<div>
<div className="pb-2">
<Button
onClick={handlePhotoGalleryButtonClick}
variant="outline-primary"
Expand All @@ -240,6 +253,18 @@ const PhotoInput: FC<PhotoInputProps> = ({
</Button>
</div>
)}
{notes && (
<TextInput
id={id}
label="Optional note about photo(s):"
value={noteValue ? noteValue?.toString() : ''}
updateValue={updateNoteValue}
min={0}
max={300}
regexp={/.*/} //any string
/>
)}

<Modal
show={showDeleteConfirmation}
onHide={cancelDeletePhoto}
Expand Down
29 changes: 29 additions & 0 deletions src/components/photo_input_wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,32 @@ import PhotoInput from './photo_input'
import PhotoMetadata from '../types/photo_metadata.type'

import { getMetadataFromPhoto, photoProperties } from '../utilities/photo_utils'
import JSONValue from '../types/json_value.type'

interface PhotoInputWrapperProps {
children: React.ReactNode
id: string
label: string
uploadable: boolean
count?: number
notes?: boolean
}

interface JSONObject {
[key: string]: JSONValue
}
//This function takes a data object from the StoreContext and converts it into an object
//whose values we can access via string-type keys
function convertDataObject(data: {}): JSONObject {
let jsonObject = data as JSONObject
let newDataObject: JSONObject = {}

for (const key in jsonObject) {
if (jsonObject.hasOwnProperty(key)) {
newDataObject[key] = jsonObject[key]
}
}
return newDataObject
}

/**
Expand All @@ -26,13 +45,15 @@ interface PhotoInputWrapperProps {
* @param label The label of the PhotoInput component
* @param uploadable When set, the PhotoInput component will open the gallery to upload the photo.
* When unset, the PhotoInput component will use device camera for taking new photo.
* @param note Boolean from the mdx component that indicates whether the notes field will be available
*/
const PhotoInputWrapper: FC<PhotoInputWrapperProps> = ({
children,
id,
label,
uploadable,
count = 10,
notes,
}) => {
const [loading, setLoading] = useState(false) // Loading state
const [error, setError] = useState('') // Loading state
Expand Down Expand Up @@ -109,6 +130,8 @@ const PhotoInputWrapper: FC<PhotoInputWrapperProps> = ({
attachments,
upsertAttachment,
deleteAttachment,
data,
lindsayJohnstonPnnl marked this conversation as resolved.
Show resolved Hide resolved
upsertData,
}) => {
const deletePhoto = (photoId: string) => {
deleteAttachment(photoId)
Expand Down Expand Up @@ -209,6 +232,12 @@ const PhotoInputWrapper: FC<PhotoInputWrapperProps> = ({
loading={loading}
error={error}
count={count}
updateNoteValue={(value: any) =>
upsertData(`${id}_note`, value)
}
noteValue={convertDataObject(data)[`${id}_note`]}
id={id}
notes={notes}
>
{children}
</PhotoInput>
Expand Down
45 changes: 33 additions & 12 deletions src/components/photo_wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { StoreContext } from './store'
import Photo from './photo'
import { useDB } from '../utilities/database_utils'

import JSONValue from '../types/json_value.type'

interface PhotoWrapperProps {
children: React.ReactNode
id: string
Expand All @@ -14,6 +16,23 @@ interface PhotoWrapperProps {
fromParent?: boolean
}

interface JSONObject {
[key: string]: JSONValue
}
//This function takes a data object from the StoreContext and converts it into an object
//whose values we can access via string-type keys
function convertDataObject(data: {}): JSONObject {
let jsonObject = data as JSONObject
let newDataObject: JSONObject = {}

for (const key in jsonObject) {
if (jsonObject.hasOwnProperty(key)) {
newDataObject[key] = jsonObject[key]
}
}
return newDataObject
}

/**
* A component that wraps a Photo component in order to tie it to the data store
*
Expand Down Expand Up @@ -125,18 +144,20 @@ const PhotoWrapper: FC<PhotoWrapperProps> = ({
return (
<StoreContext.Consumer>
{({ attachments, data }) => {
return (
<Photo
description={children}
label={label}
photos={
fromParent
? matchingAttachments
: getMatchingAttachments(attachments, id)
}
required={required}
/>
)
if (data)
return (
<Photo
description={children}
label={label}
photos={
fromParent
? matchingAttachments
: getMatchingAttachments(attachments, id)
}
required={required}
noteValue={convertDataObject(data)[`${id}_note`]}
/>
)
}}
</StoreContext.Consumer>
)
Expand Down
4 changes: 2 additions & 2 deletions src/components/text_input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ interface TextInputProps {
* @param updateValue A function called whenever the user changes the
* input value. The function has the new input value as the sole arguement.
* @param value The input value
* @param min The minimum allowed value for the input field.
* @param max The maximum allowed value for the input field.
* @param min The minimum allowed number of characters for the input field.
* @param max The maximum allowed number of characters for the input field.
* @param regexp The regular expression pattern to validate the input string.
*/
const TextInput: FC<TextInputProps> = ({
Expand Down
Loading
Loading