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

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
521 changes: 269 additions & 252 deletions src/App.css

Large diffs are not rendered by default.

27 changes: 24 additions & 3 deletions src/components/photo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,15 @@ interface PhotoProps {
label: string
photos: { id: string; photo: Blob; metadata: PhotoMetadata }[] // Array of photo objects with metadata
required: boolean
noteValue: string | undefined
}

/**
* A component that displays a photo, timestamp, geolocation, label, and description
* Displays multiple photos (2 per row) with metadata in a grid layout
*
* @param description Content (most commonly markdown text) used to describe the photo
* @param label Label for the component
* @param metadata Photo metadata including timestamp and geolocation
* @param notes User notes associated with the photo
* @param photos Array of photo objects with photo Blob and metadata for each photo
* {
* id attachment id for the photo and metadata
Expand All @@ -30,8 +29,16 @@ 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,
}) => {
const noteValueArray = noteValue?.split(`\n`)
return (photos && photos.length > 0) || required ? (
<Card className="photo-card">
<Card.Body>
Expand Down Expand Up @@ -98,6 +105,20 @@ const Photo: FC<PhotoProps> = ({ description, label, photos, required }) => {
</Row>
)
: required && <em>Missing Photo</em>}
{noteValue && (
<div className="photo-notes">
<h3>Notes: </h3>
<div>
{noteValueArray
? noteValueArray.map(string => (
<p className="photo-note-string">
{string}
</p>
))
: null}
</div>
</div>
)}
</Card.Body>
</Card>
) : null
Expand Down
20 changes: 18 additions & 2 deletions src/components/photo_input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ 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 TextInputWrapper from './text_input_wrapper'

interface PhotoInputProps {
children: React.ReactNode
Expand All @@ -21,6 +22,8 @@ interface PhotoInputProps {
loading: boolean
error: string
count: number
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 +42,21 @@ 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 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,
id,
notes = true,
}) => {
// Create references to the hidden file inputs
const hiddenPhotoCaptureInputRef = useRef<HTMLInputElement>(null)
Expand Down Expand Up @@ -231,7 +237,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 +246,16 @@ const PhotoInput: FC<PhotoInputProps> = ({
</Button>
</div>
)}
{notes && (
<TextInputWrapper
path={`${id}_note`}
label="Optional note about photo(s):"
min={0}
max={300}
regexp={/.*/} //any string
/>
)}

<Modal
show={showDeleteConfirmation}
onHide={cancelDeletePhoto}
Expand Down
7 changes: 6 additions & 1 deletion src/components/photo_input_wrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { FC, useEffect, useState } from 'react'
import React, { FC, useState } from 'react'

import imageCompression from 'browser-image-compression'

Expand All @@ -14,6 +14,7 @@ interface PhotoInputWrapperProps {
label: string
uploadable: boolean
count?: number
notes?: boolean
}

/**
Expand All @@ -26,13 +27,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 notes 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 @@ -209,6 +212,8 @@ const PhotoInputWrapper: FC<PhotoInputWrapperProps> = ({
loading={loading}
error={error}
count={count}
id={id}
notes={notes}
>
{children}
</PhotoInput>
Expand Down
34 changes: 19 additions & 15 deletions src/components/photo_wrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { FC, useEffect, useState } from 'react'

import { get } from 'lodash'
import { StoreContext } from './store'
import Photo from './photo'
import { useDB } from '../utilities/database_utils'
Expand Down Expand Up @@ -35,7 +35,6 @@ const PhotoWrapper: FC<PhotoWrapperProps> = ({
parent,
}) => {
const [matchingAttachments, setMatchingAttachments] = useState<any>({})
const [projectDoc, setProjectDoc] = useState<any>(parent)
const db = useDB()

useEffect(() => {
Expand Down Expand Up @@ -88,7 +87,6 @@ const PhotoWrapper: FC<PhotoWrapperProps> = ({

setMatchingAttachments(matchingAttachments)
// Set the filtered attachments in state
setProjectDoc(doc) // Set the full document if needed
})
.catch((err: any) => {
console.error('Failed to get matching attachments:', err)
Expand Down Expand Up @@ -123,18 +121,24 @@ const PhotoWrapper: FC<PhotoWrapperProps> = ({
return (
<StoreContext.Consumer>
{({ attachments, data }) => {
return (
<Photo
description={children}
label={label}
photos={
parent
? matchingAttachments
: getMatchingAttachments(attachments, id)
}
required={required}
/>
)
if (data)
return (
<Photo
description={children}
label={label}
photos={
parent
? matchingAttachments
: getMatchingAttachments(attachments, id)
}
required={required}
noteValue={
get(data, `${id}_note`)
? get(data, `${id}_note`)
: ''
}
/>
)
}}
</StoreContext.Consumer>
)
Expand Down
28 changes: 24 additions & 4 deletions src/components/text_input.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FC, useState } from 'react'
import { FC, useEffect, useState } from 'react'
import FloatingLabel from 'react-bootstrap/FloatingLabel'
import Form from 'react-bootstrap/Form'

Expand All @@ -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 All @@ -33,6 +33,11 @@ const TextInput: FC<TextInputProps> = ({
max,
regexp,
}) => {
const [floatingLabelClasses, setFloatingLabelClasses] =
useState<any>('mb-3')

const [textAreaFocused, setTextAreaFocused] = useState<boolean>(false)

const [error, setError] = useState<string>('')

const handleChange = (inputValue: string) => {
Expand All @@ -50,15 +55,30 @@ const TextInput: FC<TextInputProps> = ({
}
}

useEffect(() => {
let floatingLabelClasses = 'mb-3'
if (value || textAreaFocused) {
floatingLabelClasses += ' label-hidden'
}

if (textAreaFocused) {
floatingLabelClasses += ' text-area-expanded'
}

setFloatingLabelClasses(floatingLabelClasses)
}, [value, textAreaFocused])

return (
<>
<FloatingLabel className="mb-3" controlId={id} label={label}>
<FloatingLabel className={floatingLabelClasses} label={label}>
<Form.Control
as="textarea"
onChange={event => handleChange(event.target.value)}
placeholder="A placeholder"
value={value || ''}
isInvalid={Boolean(error)}
onFocus={() => setTextAreaFocused(true)}
onBlur={() => setTextAreaFocused(false)}
/>
{error && (
<Form.Control.Feedback type="invalid">
Expand Down
22 changes: 11 additions & 11 deletions src/templates/reusable/project_info_inputs.mdx
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@

#### New Project Information
<DocNameInput label="Project Name" path="doc_name" required/>

##### Installer Information
*The Installer information is optional, but we recomment filling in at least one
field for reference in the the final report.*

<DocNameInput label="Project Name" path="doc_name" required />
##### Installer Information *The Installer information is optional, but we
recommend filling in at least one field for reference in the the final report.*
<StringInput label="Technician Name" path="installer.name" />
<StringInput label="Installation Company" path="installer.company_name" />
<StringInput label="Company Address" path="installer.mailing_address" />
<StringInput label="Company Phone" path="installer.phone" />
<StringInput label="Company Email" path="installer.email" />

##### Project Address

<StringInput label="Street Address" path="location.street_address" />
<StringInput label="City" path="location.city" />
<USStateSelect label="State" path="location.state" />
<StringInput label="Zip Code" path="location.zip_code" />

<PhotoInput id="building_number_photo" label="Building Number – Photo">
Provide a photo of the building that shows the building number.
<PhotoInput
id="building_number_photo"
label="Building Number – Photo"
notes={false}
>
Provide a photo of the building that shows the building number.
</PhotoInput>

<SaveCancelButton path="status" />



Loading
Loading