Implement patch endpoints and "editables"
This commit is contained in:
parent
943bcfc1d1
commit
d3ae365ed9
31
family-tracker/package-lock.json
generated
31
family-tracker/package-lock.json
generated
@ -2435,6 +2435,37 @@
|
||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz",
|
||||
"integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA=="
|
||||
},
|
||||
"axios": {
|
||||
"version": "0.19.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz",
|
||||
"integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==",
|
||||
"requires": {
|
||||
"follow-redirects": "1.5.10"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.5.10",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
|
||||
"integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
|
||||
"requires": {
|
||||
"debug": "=3.1.0"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
}
|
||||
}
|
||||
},
|
||||
"axobject-query": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz",
|
||||
|
@ -6,6 +6,7 @@
|
||||
"@testing-library/jest-dom": "^4.2.4",
|
||||
"@testing-library/react": "^9.5.0",
|
||||
"@testing-library/user-event": "^7.2.1",
|
||||
"axios": "^0.19.2",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-router-dom": "^5.2.0",
|
||||
|
@ -10,7 +10,6 @@
|
||||
content="Family Tracker"
|
||||
/>
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
|
@ -1,3 +1,12 @@
|
||||
.mouseover:hover{
|
||||
background-color:#eee;
|
||||
}
|
||||
|
||||
.editable:hover{
|
||||
border: 1px solid blue;
|
||||
cursor:text;
|
||||
}
|
||||
|
||||
.App {
|
||||
text-align: center;
|
||||
}
|
||||
|
@ -10,7 +10,10 @@ import {
|
||||
useParams
|
||||
} from "react-router-dom";
|
||||
|
||||
function Map() {
|
||||
const axios = require('axios');
|
||||
|
||||
function Map(p) {
|
||||
p.setActive("/map")
|
||||
return (
|
||||
<>
|
||||
<h1>Map View</h1>
|
||||
@ -18,26 +21,160 @@ function Map() {
|
||||
);
|
||||
}
|
||||
|
||||
function Message() {
|
||||
function Message(p) {
|
||||
p.setActive("/messages")
|
||||
return (
|
||||
<>
|
||||
<h1>Message View</h1>
|
||||
<h1>Message View</h1>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function Member() {
|
||||
function Member(p) {
|
||||
|
||||
const [familyId,setFamilyId] = useState(0)
|
||||
const [firstName,setFirstName] = useState("")
|
||||
const [lastName,setLastName] = useState("")
|
||||
const [relationship,setRelationship] = useState("")
|
||||
const [mobileDevice,setMobileDevice] = useState("")
|
||||
const [disabled,setDisabled] = useState(false)
|
||||
|
||||
useEffect(()=>{
|
||||
p.setActive("/members")
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>Member View</h1>
|
||||
<div className="row">
|
||||
<div className="col-3 text-right">
|
||||
<label htmlFor="family">Family: </label>
|
||||
</div>
|
||||
<div className="col-9 text-left">
|
||||
<select id="family" name="family" value={familyId} onChange={(e)=>{setFamilyId(e.target.value)}}>
|
||||
{p.family.map((fam)=><option key={fam.id-1} value={fam.id-1}>{fam.name}</option>)}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="col-12">
|
||||
{(p.family[familyId])?p.family[familyId].members.map((member)=><Mem key={member.id} member={member} update={p.update} setUpdate={p.setUpdate}/>):<></>}
|
||||
</div>
|
||||
</div>
|
||||
<div className="row">
|
||||
<div className="col-sm-12 col-lg-8">
|
||||
<label htmlFor="name">Add Member:</label>
|
||||
<br/><input type="text" disabled={disabled} onChange={(e)=>{setFirstName(e.target.value);}} value={firstName} id="firstName" style={{width:"30%"}}/><input type="text" disabled={disabled} onChange={(e)=>{setLastName(e.target.value);}} value={lastName} id="lastName" style={{width:"30%"}}/><input type="text" disabled={disabled} onChange={(e)=>{setMobileDevice(e.target.value);}} value={mobileDevice} id="mobileDevice" style={{width:"30%"}}/><input type="text" disabled={disabled} onChange={(e)=>{setRelationship(e.target.value);}} value={relationship} id="relationship" style={{width:"30%"}}/><button disabled={disabled} style={{width:"10%"}} onClick={()=>{
|
||||
setDisabled(true)
|
||||
axios.post("http://localhost:8080/member/create",{firstName:firstName,lastName:lastName,mobileId:mobileDevice})
|
||||
.then((data)=>{
|
||||
return axios.post("http://localhost:8080/relationship/"+(Number(familyId)+1)+"/"+(data.data.id)+"/"+(relationship))})
|
||||
.then((data)=>{
|
||||
p.setUpdate(!p.update)
|
||||
setFirstName("")
|
||||
setLastName("")
|
||||
setMobileDevice("")
|
||||
setRelationship("")
|
||||
setDisabled(false)
|
||||
})}}>Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function Family() {
|
||||
function Mem(p) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="row mouseover">
|
||||
<div className="col-4 text-right">
|
||||
<Editable update={p.update} setUpdate={p.setUpdate} endpoint={"http://localhost:8080/member/"+p.member.id} field="firstName" value={p.member.firstName}/>
|
||||
</div>
|
||||
<div className="col-4">
|
||||
<Editable update={p.update} setUpdate={p.setUpdate} endpoint={"http://localhost:8080/member/"+p.member.id} field="lastName" value={p.member.lastName}/>
|
||||
</div>
|
||||
<div className="col-2">
|
||||
<Editable update={p.update} setUpdate={p.setUpdate} endpoint={"http://localhost:8080/member/"+p.member.id} field="mobileId" value={p.member.mobileDeviceId}/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function Editable(p) {
|
||||
const [value,setValue] = useState((typeof(p.value)==="string"&&p.value.length>0||typeof(p.value)==="number")?p.value:"-");
|
||||
const [editing,setEditing] = useState(false);
|
||||
const [disabled,setDisabled] = useState(false);
|
||||
const [style,setStyle] = useState({});
|
||||
|
||||
return (
|
||||
<>
|
||||
{(editing)?<><input type="text" style={style} value={value} disabled={disabled} onChange={(e)=>{setValue(e.target.value);setStyle({})}} onKeyPress={(e)=>{
|
||||
if (e.key==='Enter') {
|
||||
if (value.length===0) {
|
||||
return new Error("Cannot accept 0 length string.")
|
||||
}
|
||||
setDisabled(true)
|
||||
var obj = {}
|
||||
obj[p.field] = value
|
||||
axios.patch(p.endpoint,obj)
|
||||
.then((data)=>{
|
||||
p.setUpdate(!p.update)
|
||||
setDisabled(false)
|
||||
setEditing(false)
|
||||
})
|
||||
.catch((err)=>{
|
||||
setDisabled(false)
|
||||
setStyle({border:"1px solid red"})
|
||||
})
|
||||
}
|
||||
}
|
||||
}/></>:<>
|
||||
<span className="editable" onClick={()=>{setEditing(true)}}>{value}</span>
|
||||
</>}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function Fam(p) {
|
||||
return (<>
|
||||
<div className="row">
|
||||
<div className="col-1 border-left border-top border-bottom">
|
||||
{p.family.id}
|
||||
</div>
|
||||
<div className="col-1 border-top border-bottom">
|
||||
<Editable update={p.update} setUpdate={p.setUpdate} endpoint={"http://localhost:8080/family/"+p.family.id} field="name" value={p.family.name}/>
|
||||
</div>
|
||||
<div className="col-sm-12 col-lg-6 mr-3 border-left border-top border-bottom border-right">
|
||||
{p.family.members.map((member)=><Mem key={member.id} member={member} update={p.update} setUpdate={p.setUpdate}/>)}
|
||||
</div>
|
||||
</div>
|
||||
</>);
|
||||
}
|
||||
|
||||
function Family(p) {
|
||||
const [family,setFamily] = useState("");
|
||||
|
||||
useEffect(()=>{
|
||||
p.setActive("/")
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
<h1>Family View</h1>
|
||||
{p.family.map((family)=><Fam key={family.id} family={family} update={p.update} setUpdate={p.setUpdate}/>)}
|
||||
<br/>
|
||||
<div className="row">
|
||||
<div className="col-sm-12 col-lg-8">
|
||||
<label htmlFor="name">Add Family Name:</label>
|
||||
<br/><input type="text" onChange={(e)=>{setFamily(e.target.value);}} value={family} id="name" style={{width:"90%"}}/><button style={{width:"10%"}} onClick={()=>{
|
||||
axios.post("http://localhost:8080/family",{name:family})
|
||||
.then((data)=>{
|
||||
p.setUpdate(!p.update)
|
||||
setFamily("")})}}>Add</button>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@ -57,29 +194,37 @@ function L(p) {
|
||||
function App() {
|
||||
const [pageView,setPageView] = useState(null);
|
||||
const [active,setActive] = useState("/");
|
||||
const [family,setFamily] = useState([]);
|
||||
const [update,setUpdate] = useState(false);
|
||||
|
||||
useEffect(()=>{
|
||||
axios.get("http://localhost:8080/family")
|
||||
.then((data)=>{setFamily(data.data)})
|
||||
},[update])
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<div className="container-fluid">
|
||||
<div className="row">
|
||||
<div className="col-sm-3 text-center pt-5">
|
||||
<div className="col-sm-3 text-center pt-5 order-2 order-sm-2 order-md-1">
|
||||
<Link to="/" onClick={()=>{setActive("/")}}><L highlight="/" active={active} name="Family"/></Link>
|
||||
<Link to="/members" onClick={()=>{setActive("/members")}}><L highlight="/members" active={active} name="Members"/></Link>
|
||||
<Link to="/messages" onClick={()=>{setActive("/messages")}}><L highlight="/messages" active={active} name="Notifications"/></Link>
|
||||
<Link to="/map" onClick={()=>{setActive("/map")}}><L highlight="/map" active={active} name="Map"/></Link>
|
||||
</div>
|
||||
<div className="col-sm-9">
|
||||
<div className="col-sm-9 order-1 order-sm-1 order-md-2">
|
||||
<Switch>
|
||||
<Route path="/map">
|
||||
<Map/>
|
||||
<Map setActive={setActive}/>
|
||||
</Route>
|
||||
<Route path="/messages">
|
||||
<Message/>
|
||||
<Message setActive={setActive}/>
|
||||
</Route>
|
||||
<Route path="/members">
|
||||
<Member/>
|
||||
<Member setActive={setActive} family={family} update={update} setUpdate={setUpdate}/>
|
||||
</Route>
|
||||
<Route path="/">
|
||||
<Family/>
|
||||
<Family setActive={setActive} family={family} update={update} setUpdate={setUpdate}/>
|
||||
</Route>
|
||||
</Switch>
|
||||
</div>
|
||||
|
@ -21,9 +21,9 @@ public class CorsFilter extends OncePerRequestFilter {
|
||||
throws
|
||||
ServletException, IOException {
|
||||
response.addHeader("Access-Control-Allow-Origin", "*");
|
||||
response.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, PATCH, HEAD");
|
||||
response.addHeader("Access-Control-Allow-Headers", "Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers");
|
||||
response.addHeader("Access-Control-Expose-Headers", "Access-Control-Allow-Origin, Access-Control-Allow-Credentials");
|
||||
response.addHeader("Access-Control-Allow-Methods", "*");
|
||||
response.addHeader("Access-Control-Allow-Headers", "*");
|
||||
response.addHeader("Access-Control-Expose-Headers", "*");
|
||||
response.addHeader("Access-Control-Allow-Credentials", "true");
|
||||
response.addIntHeader("Access-Control-Max-Age", 10);
|
||||
filterChain.doFilter(request, response);
|
||||
|
@ -158,6 +158,56 @@ public class Endpoints {
|
||||
}
|
||||
}
|
||||
|
||||
@PatchMapping("/member/{id}")
|
||||
/**
|
||||
* @RequestBody can have:
|
||||
* firstName - (Optional)Modified first name.
|
||||
* lastName - (Optional)Modified last name.
|
||||
* mobileId - (Optional)Modified mobile Id.
|
||||
* @return
|
||||
*/
|
||||
public FamilyMember _11(
|
||||
@PathVariable Long id,
|
||||
@RequestBody HashMap<String,String> body) {
|
||||
if (members.existsById(id)) {
|
||||
FamilyMember m = members.findById(id).get();
|
||||
if (body.containsKey("firstName")) {
|
||||
m.setFirstName(body.get("firstName"));
|
||||
}
|
||||
if (body.containsKey("lastName")) {
|
||||
m.setLastName(body.get("lastName"));
|
||||
}
|
||||
if (body.containsKey("mobileId")) {
|
||||
m.setMobileDeviceId(Long.parseLong(body.get("mobileId")));
|
||||
}
|
||||
members.save(m);
|
||||
return m;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@PatchMapping("/family/{id}")
|
||||
/**
|
||||
* @RequestBody can have:
|
||||
* name - (Optional)New family name.
|
||||
* @return
|
||||
*/
|
||||
public Family _12(
|
||||
@PathVariable Long id,
|
||||
@RequestBody HashMap<String,String> body) {
|
||||
if (families.existsById(id)) {
|
||||
Family m = families.findById(id).get();
|
||||
if (body.containsKey("name")) {
|
||||
m.setName(body.get("name"));
|
||||
}
|
||||
families.save(m);
|
||||
return m;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@PostMapping("/location")
|
||||
/**
|
||||
* @RequestBody requires:
|
||||
|
@ -20,7 +20,7 @@ import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class FamilyContainer extends Family{
|
||||
|
||||
List<FamilyMember> members = new ArrayList<>();
|
||||
List<FamilyMemberContainer> members = new ArrayList<>();
|
||||
Long id = -1l;
|
||||
|
||||
FamilyContainer(String name
|
||||
@ -31,15 +31,16 @@ public class FamilyContainer extends Family{
|
||||
id = families.findByName(name).get(0).getId();
|
||||
List<FamilyRelationship> relations = relationships.findByFamilyId(id);
|
||||
for (FamilyRelationship r : relations) {
|
||||
this.members.add(members.findById(r.getMemberId()).get());
|
||||
FamilyMember m = members.findById(r.getMemberId()).get();
|
||||
this.members.add(new FamilyMemberContainer(m,relationships));
|
||||
}
|
||||
}
|
||||
|
||||
public List<FamilyMember> getMembers() {
|
||||
public List<FamilyMemberContainer> getMembers() {
|
||||
return members;
|
||||
}
|
||||
|
||||
public void setMembers(List<FamilyMember> members) {
|
||||
public void setMembers(List<FamilyMemberContainer> members) {
|
||||
this.members = members;
|
||||
}
|
||||
|
||||
|
76
src/main/java/sig/family/FamilyMemberContainer.java
Normal file
76
src/main/java/sig/family/FamilyMemberContainer.java
Normal file
@ -0,0 +1,76 @@
|
||||
package sig.family;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
|
||||
@RequestMapping
|
||||
public class FamilyMemberContainer extends FamilyMember{
|
||||
Long id;
|
||||
|
||||
String firstName,lastName;
|
||||
|
||||
Long mobileDeviceId;
|
||||
|
||||
Long lastLocationId;
|
||||
|
||||
String relationship;
|
||||
|
||||
FamilyMemberContainer(){}
|
||||
|
||||
public FamilyMemberContainer(FamilyMember m,
|
||||
FamilyRelationshipRepository relationships) {
|
||||
this.firstName = m.firstName;
|
||||
this.lastName = m.lastName;
|
||||
this.mobileDeviceId = m.mobileDeviceId;
|
||||
this.id=m.id;
|
||||
relationship = relationships.findByMemberId(m.getId()).get(0).getRelationship();
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
public String getFirstName() {
|
||||
return firstName;
|
||||
}
|
||||
public void setFirstName(String firstName) {
|
||||
this.firstName = firstName;
|
||||
}
|
||||
public String getLastName() {
|
||||
return lastName;
|
||||
}
|
||||
public void setLastName(String lastName) {
|
||||
this.lastName = lastName;
|
||||
}
|
||||
public Long getMobileDeviceId() {
|
||||
return mobileDeviceId;
|
||||
}
|
||||
public void setMobileDeviceId(Long mobileDeviceId) {
|
||||
this.mobileDeviceId = mobileDeviceId;
|
||||
}
|
||||
|
||||
public Long getLastLocationId() {
|
||||
return lastLocationId;
|
||||
}
|
||||
|
||||
public void setLastLocationId(Long lastLocationId) {
|
||||
this.lastLocationId = lastLocationId;
|
||||
}
|
||||
|
||||
public String getRelationship() {
|
||||
return relationship;
|
||||
}
|
||||
|
||||
public void setRelationship(String relationship) {
|
||||
this.relationship = relationship;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user