Autocomplete for tune attributes (#847)
This commit is contained in:
parent
6d42bc12bb
commit
05e51fc3b5
|
@ -110,12 +110,32 @@ const useDb = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const autocomplete = async (attribute: string, search: string) => {
|
||||||
|
try {
|
||||||
|
const items = await client.records.getFullList(Collections.Tunes, 10, {
|
||||||
|
filter: `${attribute} ~ "${search}"`,
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.resolve(items as TunesRecordFull[]);
|
||||||
|
} catch (error) {
|
||||||
|
if ((error as ClientResponseError).isAbort) {
|
||||||
|
return Promise.reject(new Error('Cancelled'));
|
||||||
|
}
|
||||||
|
|
||||||
|
Sentry.captureException(error);
|
||||||
|
databaseGenericError(new Error(formatError(error)));
|
||||||
|
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
updateTune: (tuneId: string, data: TunesRecordPartial): Promise<void> => updateTune(tuneId, data),
|
updateTune: (tuneId: string, data: TunesRecordPartial): Promise<void> => updateTune(tuneId, data),
|
||||||
createTune: (data: TunesRecord): Promise<TunesRecordFull> => createTune(data),
|
createTune: (data: TunesRecord): Promise<TunesRecordFull> => createTune(data),
|
||||||
getTune: (tuneId: string): Promise<TunesRecordFull | null> => getTune(tuneId),
|
getTune: (tuneId: string): Promise<TunesRecordFull | null> => getTune(tuneId),
|
||||||
getIni: (tuneId: string): Promise<IniFilesRecordFull | null> => getIni(tuneId),
|
getIni: (tuneId: string): Promise<IniFilesRecordFull | null> => getIni(tuneId),
|
||||||
searchTunes: (search: string, page: number, perPage: number): Promise<{ items: TunesRecordFull[]; totalItems: number }> => searchTunes(search, page, perPage),
|
searchTunes: (search: string, page: number, perPage: number): Promise<{ items: TunesRecordFull[]; totalItems: number }> => searchTunes(search, page, perPage),
|
||||||
|
autocomplete: (attribute: string, search: string): Promise<TunesRecordFull[]> => autocomplete(attribute, search),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ import {
|
||||||
Typography,
|
Typography,
|
||||||
Upload,
|
Upload,
|
||||||
Form,
|
Form,
|
||||||
|
AutoComplete,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import {
|
import {
|
||||||
PlusOutlined,
|
PlusOutlined,
|
||||||
|
@ -33,6 +34,7 @@ import {
|
||||||
GlobalOutlined,
|
GlobalOutlined,
|
||||||
EyeOutlined,
|
EyeOutlined,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
|
import debounce from 'lodash.debounce';
|
||||||
import { INI } from '@hyper-tuner/ini';
|
import { INI } from '@hyper-tuner/ini';
|
||||||
import { UploadRequestOption } from 'rc-upload/lib/interface';
|
import { UploadRequestOption } from 'rc-upload/lib/interface';
|
||||||
import { UploadFile } from 'antd/lib/upload/interface';
|
import { UploadFile } from 'antd/lib/upload/interface';
|
||||||
|
@ -138,7 +140,24 @@ const UploadPage = () => {
|
||||||
const { currentUser, refreshUser } = useAuth();
|
const { currentUser, refreshUser } = useAuth();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { fetchTuneFile } = useServerStorage();
|
const { fetchTuneFile } = useServerStorage();
|
||||||
const { createTune, updateTune, getTune } = useDb();
|
const { createTune, updateTune, getTune, autocomplete } = useDb();
|
||||||
|
|
||||||
|
const [autocompleteOptions, setAutocompleteOptions] = useState<{ [attribute: string]: { value: string }[] }>({});
|
||||||
|
|
||||||
|
const searchAutocomplete = debounce(async (attribute: string, search: string) => {
|
||||||
|
if (search.length === 0) {
|
||||||
|
setAutocompleteOptions((prev) => ({ ...prev, [attribute]: [] }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = (await autocomplete(attribute, search))
|
||||||
|
.map((record) => record[attribute]);
|
||||||
|
|
||||||
|
// TODO: order by occurrence (more common - higher in the list)
|
||||||
|
const unique = [...new Set(options)].map((value) => ({ value }));
|
||||||
|
|
||||||
|
setAutocompleteOptions((prev) => ({ ...prev, [attribute]: unique }));
|
||||||
|
}, 300);
|
||||||
|
|
||||||
const fetchFile = async (tuneId: string, fileName: string) => bufferToFile(await fetchTuneFile(tuneId, fileName), fileName);
|
const fetchFile = async (tuneId: string, fileName: string) => bufferToFile(await fetchTuneFile(tuneId, fileName), fileName);
|
||||||
|
|
||||||
|
@ -603,19 +622,37 @@ const UploadPage = () => {
|
||||||
<Row {...rowProps}>
|
<Row {...rowProps}>
|
||||||
<Col span={24} sm={24}>
|
<Col span={24} sm={24}>
|
||||||
<Item name="vehicleName" rules={requiredTextRules}>
|
<Item name="vehicleName" rules={requiredTextRules}>
|
||||||
<Input addonBefore="Vehicle name" />
|
<AutoComplete
|
||||||
|
options={autocompleteOptions.vehicleName}
|
||||||
|
onSearch={(search) => searchAutocomplete('vehicleName', search)}
|
||||||
|
backfill
|
||||||
|
>
|
||||||
|
<Input addonBefore="Vehicle name" />
|
||||||
|
</AutoComplete>
|
||||||
</Item>
|
</Item>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<Row {...rowProps}>
|
<Row {...rowProps}>
|
||||||
<Col {...colProps}>
|
<Col {...colProps}>
|
||||||
<Item name="engineMake" rules={requiredTextRules}>
|
<Item name="engineMake" rules={requiredTextRules}>
|
||||||
<Input addonBefore="Engine make" />
|
<AutoComplete
|
||||||
|
options={autocompleteOptions.engineMake}
|
||||||
|
onSearch={(search) => searchAutocomplete('engineMake', search)}
|
||||||
|
backfill
|
||||||
|
>
|
||||||
|
<Input addonBefore="Engine make" />
|
||||||
|
</AutoComplete>
|
||||||
</Item>
|
</Item>
|
||||||
</Col>
|
</Col>
|
||||||
<Col {...colProps}>
|
<Col {...colProps}>
|
||||||
<Item name="engineCode" rules={requiredTextRules}>
|
<Item name="engineCode" rules={requiredTextRules}>
|
||||||
<Input addonBefore="Engine code" />
|
<AutoComplete
|
||||||
|
options={autocompleteOptions.engineCode}
|
||||||
|
onSearch={(search) => searchAutocomplete('engineCode', search)}
|
||||||
|
backfill
|
||||||
|
>
|
||||||
|
<Input addonBefore="Engine code" />
|
||||||
|
</AutoComplete>
|
||||||
</Item>
|
</Item>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
@ -650,12 +687,24 @@ const UploadPage = () => {
|
||||||
<Row {...rowProps}>
|
<Row {...rowProps}>
|
||||||
<Col {...colProps}>
|
<Col {...colProps}>
|
||||||
<Item name="fuel">
|
<Item name="fuel">
|
||||||
<Input addonBefore="Fuel" />
|
<AutoComplete
|
||||||
|
options={autocompleteOptions.fuel}
|
||||||
|
onSearch={(search) => searchAutocomplete('fuel', search)}
|
||||||
|
backfill
|
||||||
|
>
|
||||||
|
<Input addonBefore="Fuel" />
|
||||||
|
</AutoComplete>
|
||||||
</Item>
|
</Item>
|
||||||
</Col>
|
</Col>
|
||||||
<Col {...colProps}>
|
<Col {...colProps}>
|
||||||
<Item name="ignition">
|
<Item name="ignition">
|
||||||
<Input addonBefore="Ignition" />
|
<AutoComplete
|
||||||
|
options={autocompleteOptions.ignition}
|
||||||
|
onSearch={(search) => searchAutocomplete('ignition', search)}
|
||||||
|
backfill
|
||||||
|
>
|
||||||
|
<Input addonBefore="Ignition" />
|
||||||
|
</AutoComplete>
|
||||||
</Item>
|
</Item>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
Loading…
Reference in New Issue