Skip to content

Commit b9f51c2

Browse files
committed
PC-112: Fix and rename PcLogoUploader.jsx; resolve bug causing incorrect logo display
1 parent 2bcba02 commit b9f51c2

8 files changed

Lines changed: 56 additions & 60 deletions

File tree

app/assets/stylesheets/application/customer_form.scss

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
.image-placeholder {
77
max-width: 136px;
8-
max-height: 117px;
8+
width: 136px;
99

1010
label:hover {
1111
cursor: pointer;
@@ -19,3 +19,9 @@
1919
.title-input {
2020
max-width: 241px;
2121
}
22+
23+
@media (max-width: 575px) {
24+
.title-input {
25+
max-width: 100%;
26+
}
27+
}

app/assets/stylesheets/application/input.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ textarea {
6161
padding: 0;
6262
}
6363

64+
.rbt-input-hint {
65+
color: transparent;
66+
}
67+
6468
/**
6569
*** TYPEAHEAD CUSTOM STYLES ***
6670
**/

app/javascript/components/services/fetchService.js

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,20 @@ export const fetchAuthentication = {
1414
export const fetchCustomers = {
1515
index: () => get(ENDPOINTS.CUSTOMERS),
1616
upsert: (data) => post(ENDPOINTS.CUSTOMERS_UPSERT, data),
17-
upsertUseFormData: (customer) => {
17+
upsertUseFormData: async (customer) => {
1818
const formData = new FormData()
1919
const { first_name, last_name } = extractNames(customer.full_name)
2020

21-
if (customer.logo_file) formData.append('customer[logo]', customer.logo_file)
21+
if (customer.logo_url) {
22+
const response = await fetch(customer.logo_url)
23+
24+
if (response.ok) {
25+
const blob = await response.blob()
26+
formData.append('customer[logo]', blob)
27+
} else {
28+
throw new Error('Failed to fetch logo')
29+
}
30+
}
2231
formData.append('customer[company_name]', customer.company_name)
2332
formData.append('customer[first_name]', first_name)
2433
formData.append('customer[last_name]', last_name)

app/javascript/components/shared/CustomerForm.jsx

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useEffect, useState } from 'react'
22
import { Row, Col, Form, Button } from 'react-bootstrap'
3-
import { PcDropdownSelect, PcCompanyLogoUploader, PcInput } from '../ui'
3+
import { PcDropdownSelect, PcLogoUploader, PcInput } from '../ui'
44
import { EMPTY_ENTITIES, INPUT_VALIDATORS, ROUTES, STEPS } from './constants'
55
import { fetchCustomers, fetchQuotes } from '../services'
66
import { useAppHooks } from '../hooks'
@@ -29,32 +29,35 @@ export const CustomerForm = () => {
2929
customers.find((c) => c.attributes.company_name.toLowerCase() === customer.company_name.toLowerCase())?.id ||
3030
customer.company_name
3131

32-
const handleCompanyChange = (e) => {
33-
const { value } = e.target
32+
const handleCompanyChange = (selected) => {
33+
console.info('handleCompanyChange', selected)
34+
if(selected.length === 0) return
35+
36+
const { value, label } = selected[0]
3437
const selectedCustomer = customers.find((customer) => customer.id === value)
3538

3639
if (selectedCustomer) {
3740
setCustomer(selectedCustomer.attributes)
3841
} else {
39-
setCustomer({
40-
...EMPTY_ENTITIES.customer,
41-
company_name: value,
42-
})
42+
setCustomer(prev => ({ ...prev, company_name: label }))
4343
}
4444

45+
setIsNextDisabled(false)
4546
setErrors((prev) => ({ ...prev, company_name: '' }))
4647
}
4748

48-
const handleCompanyInputChange = (e) => {
49-
const { value } = e.target
49+
const handleCompanyInputChange = (text, event) => {
50+
console.info('handleCompanyInputChange', text, event)
5051

51-
if (value) {
52+
if (text) {
5253
setIsNextDisabled(false)
5354
setErrors((prev) => ({ ...prev, company_name: '' }))
5455
} else {
5556
setIsNextDisabled(true)
5657
setErrors((prev) => ({ ...prev, company_name: 'Company name is required' }))
5758
}
59+
60+
setCustomer((prev) => ({ ...prev, company_name: text }))
5861
}
5962

6063
const handleInputChange = (e) => {
@@ -70,7 +73,7 @@ export const CustomerForm = () => {
7073
const handleEmailChange = (e) => {
7174
const { value } = e.target
7275

73-
if (value && !INPUT_VALIDATORS.email.test(value)) {
76+
if (value && !INPUT_VALIDATORS.emailFormat.test(value)) {
7477
setIsNextDisabled(true)
7578
setErrors((prev) => ({ ...prev, email: 'Invalid email format' }))
7679
} else {
@@ -95,6 +98,7 @@ export const CustomerForm = () => {
9598
const file = e.target.files[0]
9699

97100
if (!file) {
101+
setIsNextDisabled(false)
98102
setErrors((prev) => ({ ...prev, logo: '' }))
99103
return
100104
}
@@ -105,7 +109,7 @@ export const CustomerForm = () => {
105109
logoErrors.push('Logo must be less than 2MB')
106110
}
107111

108-
if (!INPUT_VALIDATORS.fileType.includes(file.type)) {
112+
if (!INPUT_VALIDATORS.allowedFileTypes.includes(file.type)) {
109113
logoErrors.push('Logo must be a JPEG or PNG file')
110114
}
111115

@@ -114,12 +118,7 @@ export const CustomerForm = () => {
114118
setErrors((prev) => ({ ...prev, logo: logoErrors.join('\n') }))
115119
} else {
116120
setIsNextDisabled(false)
117-
setCustomer((prev) => ({
118-
...prev,
119-
logo_file: file,
120-
logo_url: URL.createObjectURL(file),
121-
}))
122-
121+
setCustomer((prev) => ({ ...prev, logo_url: URL.createObjectURL(file), }))
123122
setErrors((prev) => ({ ...prev, logo: '' }))
124123
}
125124
}
@@ -159,11 +158,11 @@ export const CustomerForm = () => {
159158
<div className="border rounded border-primary customer-form bg-light w-100 mb-7">
160159
<Row className="mb-6">
161160
<div className="d-flex flex-column flex-sm-row gap-6">
162-
<Col className={'image-placeholder'}>
163-
<PcCompanyLogoUploader
161+
<Col className={'image-placeholder mx-auto'}>
162+
<PcLogoUploader
164163
id="company_logo"
165164
onChange={handleLogoChange}
166-
accept={INPUT_VALIDATORS.fileType.join(',')}
165+
accept={INPUT_VALIDATORS.allowedFileTypes.join(',')}
167166
logo={customer.logo_url}
168167
error={errors.logo} />
169168
</Col>
@@ -255,7 +254,9 @@ export const CustomerForm = () => {
255254
</Col>
256255
</Row>
257256
</div>
258-
<Button type={'submit'} className="pc-btn-next" disabled={isNextDisabled}>
257+
<Button type={'submit'}
258+
className="pc-btn-next mb-3"
259+
disabled={isNextDisabled}>
259260
Next
260261
</Button>
261262
</Form>

app/javascript/components/shared/constants.js

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,14 @@ export const EMPTY_ENTITIES = {
5252
position: '',
5353
address: '',
5454
notes: '',
55-
logo_file: null,
5655
logo_url: null,
5756
}
5857
// future empty entities can be added here
5958
}
6059

6160
export const INPUT_VALIDATORS = {
62-
email:/^[^\s@]+@[^\s@]+\.[^\s@]+$/,
61+
emailFormat:/^[^\s@]+@[^\s@]+\.[^\s@]+$/,
6362
maxSizeFile: 2 * 1024 * 1024,
64-
fileType: ['image/jpeg', 'image/png'],
65-
// future validation messages can be added here
63+
allowedFileTypes: ['image/jpeg', 'image/png'],
64+
// future validation rules can be added here
6665
}

app/javascript/components/ui/PcDropdownSelect.jsx

Lines changed: 3 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,41 +7,19 @@ export const PcDropdownSelect = ({
77
id,
88
options,
99
placeholder,
10-
onChange,
11-
onInputChange,
1210
height,
1311
value,
1412
label,
1513
error,
1614
maxResults = 5,
1715
hasIcon = false,
16+
...props
1817
}) => {
1918
const [isMenuOpen, setIsMenuOpen] = useState(false)
20-
const [inputValue, setInputValue] = useState('')
2119

2220
const selectedOption =
2321
options.find((opt) => opt.value === value) || (value ? { value, label: value, customOption: true } : null)
2422

25-
const hasMatchingOption = () => {
26-
const normalizedInput = normalizeName(inputValue)
27-
return options.some((option) => normalizeName(option.label) === normalizedInput)
28-
}
29-
30-
const handleChange = (selected) => {
31-
if (selected.length > 0) {
32-
const selectedOption = selected[0]
33-
const valueToPass = selectedOption.customOption ? selectedOption.label : selectedOption.value
34-
onChange({ target: { id, value: valueToPass } })
35-
} else {
36-
onChange({ target: { id, value: '' } })
37-
}
38-
}
39-
40-
const handleInputChange = (text, event) => {
41-
setInputValue(text)
42-
if (onInputChange) onInputChange(event)
43-
}
44-
4523
const handleFilterBy = (option, props) => {
4624
const inputValue = normalizeName(props.text)
4725
return normalizeName(option.label).includes(inputValue)
@@ -53,17 +31,16 @@ export const PcDropdownSelect = ({
5331
id={id}
5432
options={options}
5533
placeholder={placeholder}
56-
onChange={handleChange}
57-
onInputChange={handleInputChange}
5834
filterBy={handleFilterBy}
59-
allowNew={!hasMatchingOption()}
35+
allowNew={false}
6036
newSelectionPrefix="Add new customer: "
6137
className={`${hasIcon ? isMenuOpen ? 'pc-typeahead-arrow-up' : 'pc-typeahead-arrow-down' : ''} border border-primary rounded-1`}
6238
style={{ height }}
6339
selected={selectedOption ? [selectedOption] : []}
6440
maxResults={maxResults}
6541
paginate={false}
6642
onMenuToggle={(isOpen) => setIsMenuOpen(isOpen)}
43+
{...props}
6744
/>
6845
<Form.Label className="border-label fw-bold fs-11 lh-sm px-1">{label}</Form.Label>
6946
{error && <div className="text-danger fs-12">{error}</div>}

app/javascript/components/ui/PcCompanyLogoUploader.jsx renamed to app/javascript/components/ui/PcLogoUploader.jsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@ import React from 'react'
22
import { PcIcon } from './PcIcon'
33
import { Form } from 'react-bootstrap'
44

5-
export const PcCompanyLogoUploader = ({ id, logo, error, ...props }) => {
5+
export const PcLogoUploader = ({ id, logo, error, ...props }) => {
66
const logoDisplay = logo ?
7-
<img src={logo}
8-
alt="Company Logo"
7+
<img src={logo} alt="Logo"
98
className={'object-fit-contain w-100 h-100'} />
109
: <PcIcon name="placeholder" alt="Placeholder Logo" />
1110

12-
return <Form.Group className="d-flex flex-column w-100 h-100">
11+
return <Form.Group className="d-flex flex-column">
1312
<Form.Label htmlFor={id}
1413
className={'m-0 d-flex justify-content-center align-items-center h-100 w-100 bg-white border rounded border-primary p-1'}
14+
style={{ flexBasis: '117px', maxHeight: '117px' }}
1515
column={'sm'}>
1616
{logoDisplay}
1717
</Form.Label>

app/javascript/components/ui/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ export * from './PcStepIndicator'
33
export * from './PcIcon'
44
export * from './PcInput'
55
export * from './PcDropdownSelect'
6-
export * from './PcCompanyLogoUploader'
6+
export * from './PcLogoUploader'

0 commit comments

Comments
 (0)