Skip to content
Snippets Groups Projects
Commit 15129c32 authored by Dapeng's avatar Dapeng
Browse files

redesign owner details page

parent 3510dbfc
No related branches found
No related tags found
No related merge requests found
Showing
with 162 additions and 400 deletions
......@@ -15,6 +15,7 @@
*/
package org.springframework.samples.petclinic.model;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.hibernate.validator.constraints.NotEmpty;
import org.springframework.format.annotation.DateTimeFormat;
......@@ -42,7 +43,7 @@ public class Visit extends BaseEntity {
*/
@Column(name = "visit_date")
@Temporal(TemporalType.TIMESTAMP)
@DateTimeFormat(pattern = "yyyy/MM/dd")
@JsonFormat(pattern = "yyyy-MM-dd")
private Date date;
/**
......
......@@ -15,24 +15,18 @@
*/
package org.springframework.samples.petclinic.web;
import java.util.Map;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.samples.petclinic.model.Pet;
import org.springframework.http.HttpStatus;
import org.springframework.samples.petclinic.model.Visit;
import org.springframework.samples.petclinic.service.ClinicService;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
/**
* @author Juergen Hoeller
......@@ -40,60 +34,28 @@ import org.springframework.web.bind.annotation.RequestMethod;
* @author Arjen Poutsma
* @author Michael Isvy
*/
@Controller
public class VisitController {
@RestController
public class VisitResource {
private final ClinicService clinicService;
@Autowired
public VisitController(ClinicService clinicService) {
public VisitResource(ClinicService clinicService) {
this.clinicService = clinicService;
}
@InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {
dataBinder.setDisallowedFields("id");
}
/**
* Called before each and every @RequestMapping annotated method.
* 2 goals:
* - Make sure we always have fresh data
* - Since we do not use the session scope, make sure that Pet object always has an id
* (Even though id is not part of the form fields)
* @param petId
* @return Pet
*/
@ModelAttribute("visit")
public Visit loadPetWithVisit(@PathVariable("petId") int petId) {
Pet pet = this.clinicService.findPetById(petId);
Visit visit = new Visit();
pet.addVisit(visit);
return visit;
}
@PostMapping("/owners/{ownerId}/pets/{petId}/visits")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void create(
@Valid @RequestBody Visit visit,
@PathVariable("petId") int petId) {
// Spring MVC calls method loadPetWithVisit(...) before initNewVisitForm is called
@GetMapping("/owners/*/pets/{petId}/visits/new")
public String initNewVisitForm(@PathVariable("petId") int petId, Map<String, Object> model) {
return "pets/createOrUpdateVisitForm";
clinicService.findPetById(petId).addVisit(visit);
clinicService.saveVisit(visit);
}
// Spring MVC calls method loadPetWithVisit(...) before processNewVisitForm is called
@PostMapping("/owners/{ownerId}/pets/{petId}/visits/new")
public String processNewVisitForm(@Valid Visit visit, BindingResult result) {
if (result.hasErrors()) {
return "pets/createOrUpdateVisitForm";
} else {
this.clinicService.saveVisit(visit);
return "redirect:/owners/{ownerId}";
}
@GetMapping("/owners/{ownerId}/pets/{petId}/visits")
public Object visits(@PathVariable("petId") int petId) {
return clinicService.findPetById(petId).getVisits();
}
@GetMapping("/owners/*/pets/{petId}/visits")
public String showVisits(@PathVariable int petId, Map<String, Object> model) {
model.put("visits", this.clinicService.findPetById(petId).getVisits());
return "visitList";
}
}
......@@ -12,13 +12,13 @@
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular-route.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.8/angular-resource.min.js"></script>
<script src="scripts/app/app.js"></script>
<script src="scripts/app/owner-list/owner-list.component.js"></script>
<script src="scripts/app/owner-details/owner-details.component.js"></script>
<script src="scripts/app/owner-form/owner-form.component.js"></script>
<script src="scripts/app/pet-form/pet-form.component.js"></script>
<script src="scripts/app/visits/visits.component.js"></script>
<script src="scripts/app/vet-list/vet-list.component.js"></script>
</head>
......
......@@ -2,7 +2,7 @@
/* App Module */
var petClinicApp = angular.module('petClinicApp', [
'ngRoute', 'layoutNav', 'layoutFooter', 'layoutWelcome',
'ownerList', 'ownerDetails', 'ownerForm','petForm', 'vetList']);
'ownerList', 'ownerDetails', 'ownerForm', 'petForm', 'visits', 'vetList']);
petClinicApp.config(['$locationProvider', '$routeProvider', function ($locationProvider, $routeProvider) {
......@@ -22,26 +22,18 @@ petClinicApp.config(['$locationProvider', '$routeProvider', function ($locationP
template: '<pet-form></pet-form>'
}).when('/owners/:ownerId/pets/:petId', {
template: '<pet-form></pet-form>'
}).when('/owners/:ownerId/pets/:petId/visits', {
template: '<visits></visits>'
}).when('/vets', {
template: '<vet-list></vet-list>'
}).otherwise('/welcome');
}]);
angular.module('layoutWelcome', []);
angular.module("layoutWelcome").component("layoutWelcome", {
templateUrl: "scripts/app/fragments/welcome.html"
});
angular.module('layoutNav', []);
angular.module("layoutNav").component("layoutNav", {
templateUrl: "scripts/app/fragments/nav.html"
});
angular.module('layoutFooter', []);
angular.module("layoutFooter").component("layoutFooter", {
templateUrl: "scripts/app/fragments/footer.html"
['welcome', 'nav', 'footer'].forEach(function(c) {
var mod = 'layout' + c.toUpperCase().substring(0, 1) + c.substring(1);
angular.module(mod, []);
angular.module(mod).component(mod, {
templateUrl: "scripts/app/fragments/" + c + ".html"
});
});
\ No newline at end of file
......@@ -19,7 +19,7 @@
<a href="javascript:void(0)" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Owners <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#!/owners">All</a></li>
<li><a href="#!/new-owner">New</a></li>
<li><a href="#!/new-owner">Register</a></li>
</ul>
</li>
......
<p>Welcome to Petclinic </p>
<h1>Welcome to Petclinic</h1>
<img src="resources/images/pets.png" alt="pets logo" />
\ No newline at end of file
......@@ -29,41 +29,22 @@
<h2>Pets and Visits</h2>
<table class="table" style="width:600px;">
<tr ng-repeat="pet in $ctrl.owner.pets">
<td valign="top" style="width: 120px;">
<dl class="dl-horizontal">
<dt>Name</dt>
<dd>{{pet.name}}</dd>
<dt>Birth Date</dt>
<dd>{{pet.birthDate | date:'MM/dd/yyyy'}}</dd>
<dt>Type</dt>
<dd>{{pet.type.name}}</dd>
</dl>
</td>
<td valign="top">
<table class="table-condensed">
<thead>
<tr>
<th>Visit Date</th>
<th>Description</th>
</tr>
</thead>
<tr ng-repeat="visit in pet.visits">
<td>{{visit.date | date:'MM/dd/yyyy'}}</td>
<td>{{visit.description}}</td>
</tr>
<tr>
<td>
<a href="#!/owners/{{$ctrl.owner.id}}/pets/{{pet.id}}">Edit Pet</a>
</td>
<td>
<a href="...">Add Visit</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
<div ng-repeat="pet in $ctrl.owner.pets">
<h3>{{pet.name}}</h3>
<p>
{{pet.birthDate | date:'yyyy MMM dd'}} - {{pet.type.name}}
</p>
<h4>Visits</h4>
<p style="margin-left: 2em;" ng-repeat="visit in pet.visits">
<span style="font-style: italic; margin-right: 1em;">{{visit.date | date:'yyyy MMM dd'}}</span>
{{visit.description}}
</p>
<p style="margin-left: 2em;" ng-if="pet.visits.length == 0">
No visit yet
</p>
<hr/>
</div>
'use strict';
function loadOwner($scope, $resource, $stateParams) {
var ownerResource = $resource('/petclinic/owner/' + $stateParams.id);
$scope.owner = ownerResource.get();
}
/*
* Owner Search
*/
angular.module('controllers').controller('ownerSearchController', ['$scope', '$state',
function($scope, $state) {
$scope.ownerSearchForm = {};
// form always needs to be initialised
// otherwise we can't read $scope.ownerSearchForm.lastName
$scope.submitOwnerSearchForm = function() {
var lastNameValue;
$state.go('app.ownerlist', {lastName: $scope.ownerSearchForm.lastName});
}}]);
/*
* Owners List
*/
angular.module('controllers').controller('ownerListController', ['$scope', '$resource', '$stateParams',
function($scope, $resource, $stateParams) {
var destUrl = '/petclinic/owner/list?lastName=';
if(angular.isDefined($stateParams.lastName)) {
destUrl += $stateParams.lastName;
}
var ownerResource = $resource(destUrl);
$scope.ownerList = ownerResource.query();
}]);
/*
* Owners detail (used for both Editable and non-editable pages)
*/
angular.module('controllers').controller('ownerDetailController', ['$scope', '$resource', '$stateParams',
loadOwner
]);
/*
* Form used to create and edit owners
*/
angular.module('controllers').controller('ownerFormController', ['$scope', '$http', '$resource', '$stateParams', '$state',
function($scope, $http, $resource, $stateParams, $state) {
$scope.submitOwnerForm = {};
$scope.submitOwnerForm = function() {
var form = $scope.owner;
// Creating a Javascript object
var data = {
firstName: form.firstName,
lastName: form.lastName,
address: form.address,
city: form.city,
telephone: form.telephone
};
var request;
if ($state.current.name == 'app.owneredit') {
var restUrl = "/petclinic/owner/" + $stateParams.id;
request = $http.put(restUrl, data);
} else { // in case of owner creation
var restUrl = "/petclinic/owner";
request = $http.post(restUrl, data);
}
request.then(function () {
$state.go('app.ownerlist');
}, function (response) {
var error = response.data;
alert(error.error + "\r\n" + error.errors.map(function (e) {
return e.field + ": " + e.defaultMessage;
}).join("\r\n"));
});
}
if ($state.current.name == 'app.owneredit') {
loadOwner($scope, $resource, $stateParams);
}
}]);
<div class="container">
<h2>Owner Information</h2>
<table class="table table-striped" style="width:600px;">
<tr>
<th>Name</th>
<td><b>{{owner.firstName}} {{owner.lastName}}</b></td>
</tr>
<tr>
<th>Address</th>
<td>{{owner.address}}</td>
</tr>
<tr>
<th>City</th>
<td>{{owner.city}}</td>
</tr>
<tr>
<th>Telephone</th>
<td>{{owner.telephone}}</td>
</tr>
<tr>
<td>
<a class="btn btn-info" ui-sref="app.owneredit({id: owner.id})">Edit Owner</a></td>
<td>
<a ui-sref="app.petcreate({ownerid: owner.id})" class="btn btn-success">Add New Pet</a>
</td>
</tr>
</table>
<h2>Pets and Visits</h2>
<table class="table" style="width:600px;">
<tr ng-repeat="pet in owner.pets">
<td valign="top" style="width: 120px;">
<dl class="dl-horizontal">
<dt>Name</dt>
<dd>{{pet.name}}</dd>
<dt>Birth Date</dt>
<dd>{{pet.birthDate | date:'MM/dd/yyyy'}}</dd>
<dt>Type</dt>
<dd>{{pet.type.name}}</dd>
</dl>
</td>
<td valign="top">
<table class="table-condensed">
<thead>
<tr>
<th>Visit Date</th>
<th>Description</th>
</tr>
</thead>
<tr ng-repeat="visit in pet.visits">
<td>{{visit.date | date:'MM/dd/yyyy'}}</td>
<td>{{visit.description}}</td>
</tr>
<tr>
<td>
<a ui-sref="app.petedit( {ownerid: owner.id, petid: pet.id})">Edit Pet</a>
</td>
<td>
<spring:url value="/owners/{ownerId}/pets/{petId}/visits/new" var="visitUrl">
<spring:param name="ownerId" value="${owner.id}"/>
<spring:param name="petId" value="${pet.id}"/>
</spring:url>
<a href="${fn:escapeXml(visitUrl)}">Add Visit</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
<div class="container">
<h2>Owner</h2>
<form class="form-horizontal" name="ownerForm" data-ng-controller="ownerFormController">
<div class="form-group">
<label class="col-sm-2 control-label">First name</label>
<div class="col-sm-4">
<input class="form-control" ng-model="owner.firstName" name="firstName" required/>
<span ng-show="ownerForm.firstName.$error.required" class="help-inline">First name is required.</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Last name</label>
<div class="col-sm-4">
<input class="form-control" ng-model="owner.lastName" name="lastName" required/>
<span ng-show="ownerForm.lastName.$error.required" class="help-inline">Last name is required.</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Address</label>
<div class="col-sm-4">
<input class="form-control" ng-model="owner.address" name="address" required/>
<span ng-show="ownerForm.address.$error.required" class="help-inline">Address is required.</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">City</label>
<div class="col-sm-4">
<input class="form-control" ng-model="owner.city" name="city" required/>
<span ng-show="ownerForm.city.$error.required" class="help-inline">City is required.</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Telephone</label>
<div class="col-sm-4">
<input class="form-control" ng-model="owner.telephone" name="telephone" required/>
<span ng-show="ownerForm.telephone.$error.required" class="help-inline">Telephone is required.</span>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-4">
<button class="btn btn-primary" type="submit" ng-click="submitOwnerForm()">Submit</button>
</div>
</div>
</form>
</div>
<h2>Owners</h2>
<form class="form-inline" ng-controller="ownerSearchController">
<div class="control-group">
<label class="control-label">Last name</label>
<input class="form-control" type="text" ng-model="ownerSearchForm.lastName" size="30" maxlength="80"/>
<button class="btn btn-info" type="submit" ng-click="submitOwnerSearchForm()">Find Owners</button> |
<a class="btn btn-info" ui-sref="app.ownercreate">Add New Owner</a>
</div>
</form>
\ No newline at end of file
......@@ -16,9 +16,10 @@ angular.module("petForm").component("petForm", {
var petId = $routeParams.petId || 0;
if (petId) {
if (petId) { // edit
$http.get("owner/" + ownerId + "/pet/" + petId).then(function(resp) {
self.pet = resp.data;
self.pet.birthDate = new Date(self.pet.birthDate);
self.petTypeId = "" + self.pet.type.id;
});
} else {
......@@ -26,6 +27,7 @@ angular.module("petForm").component("petForm", {
self.pet = {
owner: resp.data.firstName + " " + resp.data.lastName
};
self.petTypeId = "1";
})
}
......@@ -54,6 +56,7 @@ angular.module("petForm").component("petForm", {
$location.url("owners/" + ownerId);
}, function (response) {
var error = response.data;
error.errors = error.errors || [];
alert(error.error + "\r\n" + error.errors.map(function (e) {
return e.field + ": " + e.defaultMessage;
}).join("\r\n"));
......
<h2>Pet</h2>
<form class="form-horizontal">
<form class="form-horizontal" ng-submit="$ctrl.submit()">
<div class="form-group">
<label class="col-sm-2 control-label">Owner</label>
<div class="col-sm-6">
......@@ -19,7 +19,7 @@
<div class="form-group">
<label class="col-sm-2 control-label">Birth date</label>
<div class="col-sm-6">
<input class="form-control" ng-model="$ctrl.pet.birthDate" value="{{pet.birthDate}}" required type="text"/>
<input class="form-control" ng-model="$ctrl.pet.birthDate" required type="date"/>
<span ng-show="petForm.name.$error.required" class="help-inline"> birth date is required.</span>
</div>
</div>
......@@ -35,7 +35,7 @@
<div class="form-group">
<div class="col-sm-6 col-sm-offset-2">
<button class="btn btn-primary" type="submit" ng-click="$ctrl.submit()">
<button class="btn btn-primary" type="submit">
Submit
</button>
</div>
......
'use strict';
function loadPet($scope, $resource, $stateParams) {
var petResource = $resource('/petclinic/owner/' + $stateParams.ownerid +"/pet/" + $stateParams.petid);
$scope.pet = petResource.get();
$scope.types = $resource("/petclinic/petTypes").query();
}
/*
* Form used to create and edit pets
*/
angular.module('controllers').controller('petFormController', ['$scope', '$http', '$resource', '$stateParams', '$state',
function($scope, $http, $resource, $stateParams, $state) {
$scope.submitPetForm = {};
$scope.submitPetForm = function() {
var form = $scope.pet;
// Creating a Javascript object
var data = {
name: form.name,
birthDate: form.birthDate,
type: form.type
};
if ($state.current.name == 'app.petedit') {
var restUrl = "/petclinic/owner/" + $stateParams.ownerid +"/pet/" +$stateParams.petid;
$http.put(restUrl, data);
$state.go('app.ownerdetail');
}
else { // in case of pet creation
var restUrl = "/petclinic/owner/" + $stateParams.ownerid +"/pet";
$http.post(restUrl, data);
$state.go('app.ownerdetail');
}
}
if ($state.current.name == 'app.petedit') {
loadPet($scope, $resource, $stateParams);
}
}]);
<div class="container">
<h2>Pet</h2>
<form class="form-horizontal" name="petForm" data-ng-controller="petFormController">
<div class="form-group">
<label class="col-sm-2 control-label">Owner</label>
<div class="col-sm-6">
<p class="form-control-static">{{pet.owner}}</p>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Name </label>
<div class="col-sm-6">
<input class="form-control col-sm-4" ng-model="pet.name" name="name" required type="text"/>
<span ng-show="petForm.name.$error.required" class="help-inline"> Name is required.</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Birth date </label>
<div class="col-sm-6">
<input class="form-control" ng-model="pet.birthDate" value="{{pet.birthDate}}" required type="text"/>
<span ng-show="petForm.name.$error.required" class="help-inline"> birth date is required.</span>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Type </label>
<div class="col-sm-6">
<select class="form-control" ng-model="pet.type.id">
<option ng-repeat="x in types" value="{{x.id}}">{{x.name}}</option>
</select>
</div>
</div>
</form>
</div>
'use strict';
angular.module('visits', [
'ngRoute'
]);
angular.module("visits").component("visits", {
templateUrl: "/petclinic/scripts/app/visits/visits.template.html",
controller: ["$http", '$routeParams', '$location', '$filter', function ($http, $routeParams, $location, $filter) {
var self = this;
var petId = $routeParams.petId || 0;
var url = "owners/" + ($routeParams.ownerId || 0) + "/pets/" + petId + "/visits";
self.date = new Date();
self.desc = "";
$http.get(url).then(function(resp) {
self.visits = resp.data;
});
self.submit = function() {
var data = {
date : $filter('date')(self.date, "yyyy-MM-dd"),
description : self.desc
};
console.log(data);
$http.post(url, data).then(function() {
$location.url("owners/" + $routeParams.ownerId);
}, function() {
});
};
}]
});
\ No newline at end of file
<h2>Visits</h2>
<form ng-submit="$ctrl.submit()">
<div class="form-group">
<label>Date</label>
<input type="date" class="form-control" ng-model='$ctrl.date'/>
</div>
<div class="form-group">
<label>Description</label>
<textarea class="form-control" ng-model="$ctrl.desc" style="resize:vertical;" required></textarea>
</div>
<div class="form-group">
<button class="btn btn-primary" type="submit">Add New Visit</button>
</div>
</form>
<h3>Previous Visits</h3>
<table class="table table-striped">
<tr ng-repeat="v in $ctrl.visits">
<td class="col-sm-2">{{v.date}}</td>
<td>{{v.description}}</td>
</tr>
</table>
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment