diff options
author | Anton Tananaev <anton@traccar.org> | 2023-08-19 13:58:45 -0700 |
---|---|---|
committer | Anton Tananaev <anton@traccar.org> | 2023-08-19 13:59:07 -0700 |
commit | d3c7705bedebd65c94f9eea691aaf2fe03b0cafe (patch) | |
tree | 5f98b3d9bbbd4fe8067b5a334e84aff008b8db22 /modern/src/common/components/LinkField.jsx | |
parent | 0161ae449d4a7bd0781c0665d663353663ab0faf (diff) | |
download | trackermap-web-d3c7705bedebd65c94f9eea691aaf2fe03b0cafe.tar.gz trackermap-web-d3c7705bedebd65c94f9eea691aaf2fe03b0cafe.tar.bz2 trackermap-web-d3c7705bedebd65c94f9eea691aaf2fe03b0cafe.zip |
Move to Vite
Diffstat (limited to 'modern/src/common/components/LinkField.jsx')
-rw-r--r-- | modern/src/common/components/LinkField.jsx | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/modern/src/common/components/LinkField.jsx b/modern/src/common/components/LinkField.jsx new file mode 100644 index 00000000..08c6213a --- /dev/null +++ b/modern/src/common/components/LinkField.jsx @@ -0,0 +1,93 @@ +import { Autocomplete, TextField } from '@mui/material'; +import React, { useState } from 'react'; +import { useEffectAsync } from '../../reactHelper'; + +const LinkField = ({ + label, + endpointAll, + endpointLinked, + baseId, + keyBase, + keyLink, + keyGetter = (item) => item.id, + titleGetter = (item) => item.name, +}) => { + const [active, setActive] = useState(false); + const [open, setOpen] = useState(false); + const [items, setItems] = useState(); + const [linked, setLinked] = useState(); + + useEffectAsync(async () => { + if (active) { + const response = await fetch(endpointAll); + if (response.ok) { + setItems(await response.json()); + } else { + throw Error(await response.text()); + } + } + }, [active]); + + useEffectAsync(async () => { + if (active) { + const response = await fetch(endpointLinked); + if (response.ok) { + setLinked(await response.json()); + } else { + throw Error(await response.text()); + } + } + }, [active]); + + const createBody = (linkId) => { + const body = {}; + body[keyBase] = baseId; + body[keyLink] = linkId; + return body; + }; + + const onChange = async (value) => { + const oldValue = linked.map((it) => keyGetter(it)); + const newValue = value.map((it) => keyGetter(it)); + if (!newValue.find((it) => it < 0)) { + const results = []; + newValue.filter((it) => !oldValue.includes(it)).forEach((added) => { + results.push(fetch('/api/permissions', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(createBody(added)), + })); + }); + oldValue.filter((it) => !newValue.includes(it)).forEach((removed) => { + results.push(fetch('/api/permissions', { + method: 'DELETE', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(createBody(removed)), + })); + }); + await Promise.all(results); + setLinked(value); + } + }; + + return ( + <Autocomplete + loading={active && !items} + isOptionEqualToValue={(i1, i2) => keyGetter(i1) === keyGetter(i2)} + options={items || []} + getOptionLabel={(item) => titleGetter(item)} + renderInput={(params) => <TextField {...params} label={label} />} + value={(items && linked) || []} + onChange={(_, value) => onChange(value)} + open={open} + onOpen={() => { + setOpen(true); + setActive(true); + }} + onClose={() => setOpen(false)} + multiple + /> + ); +}; + +export default LinkField; |