Merge branch 'person_renderbox_thirdparty_onthefly' into post-prototypage

This commit is contained in:
Mathieu Jaumotte 2021-09-02 12:25:33 +02:00
commit a4118b1235
15 changed files with 343 additions and 105 deletions

View File

@ -77,6 +77,8 @@ const messages = {
man: "Né le",
woman: "Née le"
},
deathdate: "Date de décès",
years_old: "ans",
no_data: "Aucune information renseignée",
type: {
thirdparty: "Tiers",

View File

@ -6,7 +6,7 @@
addEntity: false,
addLink: false,
addAltNames: true,
addAge : false,
addAge : true,
hLevel : 3,
}"
:person="participation.person">

View File

@ -16,7 +16,7 @@
addLink: false,
addId: false,
addEntity: true,
addInfo: true,
addInfo: false,
hLevel: 3,
isMultiline: true
}"

View File

@ -17,7 +17,7 @@
<third-party-render-box
v-if="resource.resource.type === 'thirdparty'"
:thirdparty="resource.resource"
:options="{ addLink : false, addId : false, addEntity: true, addInfo: true, hLevel: 3 }"
:options="{ addLink : false, addId : false, addEntity: true, addInfo: false, hLevel: 3 }"
>
<template v-slot:record-actions>
<ul class="record_actions">

View File

@ -27,15 +27,18 @@
</div>
<p v-if="this.options.addInfo == true" class="moreinfo">
<p v-if="options.addInfo == true" class="moreinfo">
<i :class="'fa fa-fw ' + getGenderIcon" title="{{ getGender }}"></i>
<time v-if="person.birthdate" datetime="{{ person.birthdate }}" title="{{ birthdate }}">
{{ $t(getGender) + ' ' + $d(birthdate, 'text') }}
<time v-if="person.birthdate && !person.deathdate" datetime="{{ person.birthdate }}" title="{{ birthdate }}">
{{ $t(getGenderTranslation) + ' ' + $d(birthdate, 'text') }}
</time>
<time v-else-if="person.deathdate" datetime="{{ person.deathdate }}" title="{{ person.deathdate }}">
<time v-else-if="person.birthdate && person.deathdate" datetime="{{ person.deathdate }}" title="{{ person.deathdate }}">
{{ birthdate }} - {{ deathdate }}
</time>
<!-- <span class="age">{{ person.age }}</span> -->
<time v-else-if="person.deathdate" datetime="{{ person.deathdate }}" title="{{ person.deathdate }}">
{{ $t('renderbox.deathdate') + ' ' + deathdate }}
</time>
<span v-if="options.addAge && person.birthdate" class="age">{{ getAge }} {{ $t('renderbox.years_old')}}</span>
</p>
</div>
</div>
@ -118,7 +121,7 @@ export default {
},
props: ['person', 'options', 'render'],
computed: {
getGender: function() {
getGenderTranslation: function() {
return this.person.gender == 'woman' ? 'renderbox.birthday.woman' : 'renderbox.birthday.man';
},
isMultiline: function() {
@ -132,13 +135,24 @@ export default {
return this.person.gender == 'woman' ? 'fa-venus' : this.person.gender == 'man' ? 'fa-mars' : 'fa-neuter';
},
birthdate: function(){
var date = new Date(this.person.birthdate.datetime);
return dateToISO(date);
if(this.person.birthdate !== null){
const date = new Date(this.person.birthdate.datetime);
return dateToISO(date)
} else {
return "";
}
},
deathdate: function(){
if (typeof this.person.deathdate !== 'undefined') {
var date = new Date(this.person.deathdate.datetime);
return dateToISO(date);
// TODO FIX edition conflict: if null or undefined ?
// if (typeof this.person.deathdate !== 'undefined') {
// var date = new Date(this.person.deathdate.datetime);
// return dateToISO(date);
//}
if(this.person.deathdate !== null){
const date = new Date(this.person.deathdate.datetime);
return date.toLocaleDateString("fr-FR");
} else {
return "";
}
},
altNameLabel: function(){
@ -151,8 +165,25 @@ export default {
return this.person.altNames[i].key
}
},
getUrl() {
getUrl: function() {
return `/fr/person/${this.person.id}/general`;
},
getAge: function(){
if(this.person.birthdate && !this.person.deathdate){
const birthday = new Date(this.person.birthdate.datetime)
const now = new Date()
return (now.getFullYear() - birthday.getFullYear())
} else if(this.person.birthdate && this.person.deathdate){
const birthday = new Date(this.person.birthdate.datetime)
const deathdate = new Date(this.person.deathdate.datetime)
return (deathdate.getFullYear() - birthday.getFullYear())
} else if(!this.person.birthdate && this.person.deathdate.datetime) {
// todo: change this
return "Age unknown"
} else {
// todo: change this
return "Age unknown"
}
}
}
}
@ -188,6 +219,12 @@ div.flex-table {
}
}
}
.age{
margin-left: 0.5em;
&:before { content: '('; }
&:after { content: ')'; }
}
</style>

View File

@ -7,6 +7,7 @@
addInfo: true,
addEntity: false,
addAltNames: true,
addAge: true,
addId: true,
addLink: false,
hLevel: 3,

View File

@ -69,6 +69,7 @@ class PersonNormalizer implements
'firstName' => $person->getFirstName(),
'lastName' => $person->getLastName(),
'birthdate' => $this->normalizer->normalize($person->getBirthdate()),
'deathdate' => $this->normalizer->normalize($person->getDeathdate()),
'center' => $this->normalizer->normalize($person->getCenter()),
'phonenumber' => $person->getPhonenumber(),
'mobilenumber' => $person->getMobilenumber(),
@ -125,6 +126,7 @@ class PersonNormalizer implements
foreach ([
'birthdate' => \DateTime::class,
'deathdate' => \DateTime::class,
'center' => Center::class
] as $item => $class) {
if (\array_key_exists($item, $data)) {

View File

@ -45,7 +45,7 @@ components:
type:
type: string
enum:
- 'person'
- "person"
firstName:
type: string
lastName:
@ -55,7 +55,9 @@ components:
description: a canonical representation for the person name
readOnly: true
birthdate:
$ref: '#/components/schemas/Date'
$ref: "#/components/schemas/Date"
deathdate:
$ref: "#/components/schemas/Date"
phonenumber:
type: string
mobilenumber:
@ -78,7 +80,7 @@ components:
type:
type: string
enum:
- 'person'
- "person"
required:
- id
- type
@ -96,7 +98,7 @@ components:
type:
type: string
enum:
- 'thirdparty'
- "thirdparty"
required:
- id
- type
@ -119,15 +121,15 @@ components:
type:
type: string
enum:
- 'accompanying_period_resource'
- "accompanying_period_resource"
readOnly: true
id:
type: integer
readOnly: true
resource:
anyOf:
- $ref: '#/components/schemas/PersonById'
- $ref: '#/components/schemas/ThirdPartyById'
- $ref: "#/components/schemas/PersonById"
- $ref: "#/components/schemas/ThirdPartyById"
ResourceById:
type: object
properties:
@ -136,7 +138,7 @@ components:
type:
type: string
enum:
- 'accompanying_period_resource'
- "accompanying_period_resource"
required:
- id
- type
@ -146,7 +148,7 @@ components:
type:
type: string
enum:
- 'accompanying_period_comment'
- "accompanying_period_comment"
readOnly: true
id:
type: integer
@ -161,7 +163,7 @@ components:
type:
type: string
enum:
- 'accompanying_period_comment'
- "accompanying_period_comment"
required:
- id
- type
@ -173,7 +175,7 @@ components:
type:
type: string
enum:
- 'social_issue'
- "social_issue"
parent_id:
type: integer
readOnly: true
@ -200,7 +202,7 @@ components:
type:
type: string
enum:
- 'household'
- "household"
HouseholdPosition:
type: object
properties:
@ -209,7 +211,7 @@ components:
type:
type: string
enum:
- 'household_position'
- "household_position"
AccompanyingCourseWork:
type: object
properties:
@ -218,7 +220,7 @@ components:
type:
type: string
enum:
- 'accompanying_period_work'
- "accompanying_period_work"
note:
type: string
startDate:
@ -243,15 +245,15 @@ components:
type:
type: string
enum:
- 'accompanying_period_work_goal'
- "accompanying_period_work_goal"
note:
type: string
goal:
$ref: '#/components/schemas/SocialWorkGoalById'
$ref: "#/components/schemas/SocialWorkGoalById"
results:
type: array
items:
$ref: '#/components/schemas/SocialWorkGoalById'
$ref: "#/components/schemas/SocialWorkGoalById"
SocialWorkResultById:
type: object
@ -261,7 +263,7 @@ components:
type:
type: string
enum:
- 'social_work_result'
- "social_work_result"
SocialWorkGoalById:
type: object
properties:
@ -270,7 +272,7 @@ components:
type:
type: string
enum:
- 'social_work_goal'
- "social_work_goal"
paths:
/1.0/person/person/{id}.json:
@ -308,7 +310,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/Person'
$ref: "#/components/schemas/Person"
responses:
200:
description: "OK"
@ -355,7 +357,6 @@ paths:
422:
description: "Unprocessable entity (validation errors)"
/1.0/person/address/suggest/by-person/{id}.json:
get:
tags:
@ -452,7 +453,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/AccompanyingPeriod'
$ref: "#/components/schemas/AccompanyingPeriod"
examples:
Set the requestor as anonymous:
value:
@ -520,8 +521,8 @@ paths:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/PersonById'
- $ref: '#/components/schemas/ThirdPartyById'
- $ref: "#/components/schemas/PersonById"
- $ref: "#/components/schemas/ThirdPartyById"
examples:
add person with id 50:
summary: "a person with id 50"
@ -585,7 +586,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/PersonById'
$ref: "#/components/schemas/PersonById"
responses:
401:
description: "Unauthorized"
@ -614,7 +615,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/PersonById'
$ref: "#/components/schemas/PersonById"
responses:
401:
description: "Unauthorized"
@ -645,7 +646,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/Resource'
$ref: "#/components/schemas/Resource"
examples:
add person with id 50:
summary: "a person with id 50"
@ -690,7 +691,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/ResourceById'
$ref: "#/components/schemas/ResourceById"
responses:
401:
description: "Unauthorized"
@ -721,7 +722,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/Comment'
$ref: "#/components/schemas/Comment"
examples:
a single comment:
summary: "a simple comment"
@ -759,7 +760,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/CommentById'
$ref: "#/components/schemas/CommentById"
responses:
401:
description: "Unauthorized"
@ -790,7 +791,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/Scope'
$ref: "#/components/schemas/Scope"
examples:
add a scope:
value:
@ -824,7 +825,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/ScopeById'
$ref: "#/components/schemas/ScopeById"
responses:
401:
description: "Unauthorized"
@ -855,7 +856,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/SocialIssue'
$ref: "#/components/schemas/SocialIssue"
examples:
add a social issue:
value:
@ -889,7 +890,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/SocialIssue'
$ref: "#/components/schemas/SocialIssue"
responses:
401:
description: "Unauthorized"
@ -926,11 +927,11 @@ paths:
type:
type: string
enum:
- 'accompanying_period_work'
- "accompanying_period_work"
startDate:
$ref: '#/components/schemas/Date'
$ref: "#/components/schemas/Date"
endDate:
$ref: '#/components/schemas/Date'
$ref: "#/components/schemas/Date"
examples:
create a work:
value:
@ -992,7 +993,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/AccompanyingCourseWork'
$ref: "#/components/schemas/AccompanyingCourseWork"
responses:
401:
description: "Unauthorized"
@ -1029,8 +1030,6 @@ paths:
400:
description: "transition cannot be applyed"
/1.0/person/accompanying-period/origin.json:
get:
tags:
@ -1064,8 +1063,6 @@ paths:
404:
description: "Not found"
/1.0/person/household.json:
get:
tags:
@ -1095,7 +1092,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/Household'
$ref: "#/components/schemas/Household"
404:
description: "not found"
401:
@ -1125,7 +1122,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/Household'
$ref: "#/components/schemas/Household"
404:
description: "not found"
401:
@ -1149,23 +1146,22 @@ paths:
type: object
properties:
person:
$ref: '#/components/schemas/PersonById'
$ref: "#/components/schemas/PersonById"
start_date:
$ref: '#/components/schemas/Date'
$ref: "#/components/schemas/Date"
position:
$ref: '#/components/schemas/HouseholdPosition'
$ref: "#/components/schemas/HouseholdPosition"
holder:
type: boolean
comment:
type: string
destination:
$ref: '#/components/schemas/Household'
$ref: "#/components/schemas/Household"
examples:
Moving person to a new household:
value:
concerned:
-
person:
- person:
id: 0
type: person
position:
@ -1180,8 +1176,7 @@ paths:
Moving person to a new household and set an address to this household:
value:
concerned:
-
person:
- person:
id: 0
type: person
position:
@ -1198,8 +1193,7 @@ paths:
Moving person to an existing household:
value:
concerned:
-
person:
- person:
id: 0
type: person
position:
@ -1215,8 +1209,7 @@ paths:
Removing a person from any household:
value:
concerned:
-
person:
- person:
id: 0
type: person
start_date:
@ -1270,8 +1263,6 @@ paths:
400:
description: "transition cannot be applyed"
/1.0/person/social/social-action.json:
get:
tags:
@ -1349,7 +1340,6 @@ paths:
404:
description: not found
/1.0/person/social-work/social-issue.json:
get:
tags:
@ -1379,7 +1369,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/SocialIssue'
$ref: "#/components/schemas/SocialIssue"
404:
description: "not found"
401:

View File

@ -8,6 +8,8 @@ use Symfony\Component\HttpKernel\DependencyInjection\Extension;
use Symfony\Component\DependencyInjection\Loader;
use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
use Chill\ThirdPartyBundle\Security\Voter\ThirdPartyVoter;
use Symfony\Component\HttpFoundation\Request;
/**
* This is the class that loads and manages your bundle configuration.
@ -47,11 +49,42 @@ class ChillThirdPartyExtension extends Extension implements PrependExtensionInte
{
//declare routes for 3party bundle
$container->prependExtensionConfig('chill_main', array(
'routing' => array(
'routing' => [
'resources' => array(
'@ChillThirdPartyBundle/config/routes.yaml'
)
)
],
'apis' => [
[
'class' => \Chill\ThirdPartyBundle\Entity\ThirdParty::class,
'name' => 'thirdparty',
'base_path' => '/api/1.0/thirdparty/thirdparty',
'base_role' => \Chill\ThirdPartyBundle\Security\Authorization\ThirdPartyVoter::class,
'controller' => \Chill\ThirdPartyBundle\Controller\ThirdPartyApiController::class,
'actions' => [
'_index' => [
'methods' => [
Request::METHOD_GET => true,
Request::METHOD_HEAD => true,
Request::METHOD_POST => true,
],
],
'_entity' => [
'methods' => [
Request::METHOD_GET => true,
Request::METHOD_HEAD => true,
Request::METHOD_POST=> true,
],
'roles' => [
Request::METHOD_GET => \Chill\ThirdPartyBundle\Security\Voter\ThirdPartyVoter::SHOW,
Request::METHOD_HEAD => \Chill\ThirdPartyBundle\Security\Voter\ThirdPartyVoter::SHOW,
Request::METHOD_POST => \Chill\ThirdPartyBundle\Security\Voter\ThirdPartyVoter::CREATE,
],
]
]
]
],
));
}

View File

@ -30,6 +30,7 @@ use Chill\MainBundle\Entity\Center;
use Symfony\Component\Validator\Constraints as Assert;
use Chill\MainBundle\Entity\Address;
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
use Symfony\Component\Serializer\Annotation\Groups;
/**
* ThirdParty is a party recorded in the database.
@ -59,6 +60,7 @@ class ThirdParty
* @var string
* @ORM\Column(name="name", type="string", length=255)
* @Assert\Length(min="2")
* @Groups({"read"})
*/
private $name;
@ -128,6 +130,7 @@ class ThirdParty
* @Assert\Regex("/^([\+{1}])([0-9\s*]{4,20})$/",
* message="Invalid phone number: it should begin with the international prefix starting with ""+"", hold only digits and be smaller than 20 characters. Ex: +33123456789"
* )
* @Groups({"read"})
*/
private $telephone;
@ -135,6 +138,7 @@ class ThirdParty
* @var string|null
* @ORM\Column(name="email", type="string", length=255, nullable=true)
* @Assert\Email(checkMX=false)
* @Groups({"read"})
*/
private $email;
@ -143,6 +147,7 @@ class ThirdParty
* @ORM\ManyToOne(targetEntity="\Chill\MainBundle\Entity\Address",
* cascade={"persist", "remove"})
* @ORM\JoinColumn(nullable=true, onDelete="SET NULL")
* @Groups({"read"})
*/
private $address;

View File

@ -0,0 +1,36 @@
/*
* GET a thirdparty by id
*/
const getThirdparty = (id) => {
const url = `/api/1.0/thirdparty/thirdparty/${id}.json`;
return fetch(url)
.then(response => {
if (response.ok) {
return response.json();
}
throw Error('Error with request resource response');
});
};
/*
* POST a new person
*/
const postThirdparty = (body) => {
const url = `/api/1.0/thirdparty/thirdparty.json`;
return fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
body: JSON.stringify(body)
})
.then(response => {
if (response.ok) { return response.json(); }
throw Error('Error with request resource response');
});
};
export {
getThirdparty,
postThirdparty
};

View File

@ -26,7 +26,7 @@
<time v-else-if="thirdparty.deathdate" datetime="{{ thirdparty.deathdate.datetime }}" title="{{ thirdparty.deathdate }}">
{{ birthdate }} - {{ deathdate }}
</time>
<span class="age">{{ thirdparty.age }}</span>
<span v-if="options.addAge == true" class="age">{{ thirdparty.age }}</span>
</p>
</div>
</div>
@ -37,9 +37,9 @@
<i class="fa fa-li fa-map-marker"></i>
<show-address :address="thirdparty.address" :isMultiline="isMultiline"></show-address>
</li>
<li v-if="thirdparty.telephone">
<li v-if="thirdparty.phonenumber">
<i class="fa fa-li fa-mobile"></i>
<a :href="'tel: ' + thirdparty.telephone">{{ thirdparty.telephone }}</a>
<a :href="'tel: ' + thirdparty.phonenumber">{{ thirdparty.phonenumber }}</a>
</li>
<li v-if="thirdparty.email">
<i class="fa fa-li fa-envelope-o"></i>

View File

@ -1,22 +1,109 @@
<template>
<div v-if="action === 'show'">
show
thirdparty
{{ id }}
<div class="flex-table">
<third-party-render-box
:thirdparty="thirdparty"
:options="{
addInfo: true,
addEntity: false,
addAltNames: true,
addId: true,
addLink: false,
addAge: false,
hLevel: 3,
addCenter: true,
addNoData: true,
isMultiline: true
}"
></third-party-render-box>
</div>
</div>
<div v-else-if="action === 'edit' || action === 'create'">
{{ action }}
thirdparty
{{ id }}
<div class="form-floating mb-3">
<input class="form-control form-control-lg" id="firstname" v-model="thirdparty.firstName" v-bind:placeholder="$t('thirdparty.firstname')" />
<label for="firstname">{{ $t('thirdparty.firstname') }}</label>
</div>
<div class="form-floating mb-3">
<input class="form-control form-control-lg" id="lastname" v-model="thirdparty.lastName" v-bind:placeholder="$t('thirdparty.lastname')" />
<label for="lastname">{{ $t('thirdparty.lastname') }}</label>
</div>
<div class="input-group mb-3">
<span class="input-group-text" id="email"><i class="fa fa-fw fa-envelope"></i></span>
<input class="form-control form-control-lg"
v-model="thirdparty.email"
v-bind:placeholder="$t('thirdparty.email')"
v-bind:aria-label="$t('thirdparty.email')"
aria-describedby="email" />
</div>
<div class="input-group mb-3">
<span class="input-group-text" id="phonenumber"><i class="fa fa-fw fa-phone"></i></span>
<input class="form-control form-control-lg"
v-model="thirdparty.phonenumber"
v-bind:placeholder="$t('thirdparty.phonenumber')"
v-bind:aria-label="$t('thirdparty.phonenumber')"
aria-describedby="phonenumber" />
</div>
</div>
</template>
<script>
import ThirdPartyRenderBox from '../Entity/ThirdPartyRenderBox.vue';
import { getThirdparty, postThirdparty } from '../../_api/OnTheFly';
export default {
name: "OnTheFlyThirdParty",
props: ['id', 'type', 'action']
props: ['id', 'type', 'action'],
components: {
ThirdPartyRenderBox,
},
data: function() {
return {
thirdparty: {
type: 'thirdparty'
}
}
},
methods: {
loadThirdparty(){
getThirdparty(this.id).then(thirdparty => new Promise((resolve, reject) => {
this.thirdparty = thirdparty;
resolve();
}));
},
postData() {
postThirdparty(this.thirdparty).then(thirdparty => new Promise((resolve, reject) => {
this.thirdparty = thirdparty;
resolve();
}))
}
},
mounted() {
if (this.action !== 'create'){
this.loadThirdparty();
}
},
}
</script>
<style lang="css" scoped>
<style lang="scss" scoped>
div.flex-table {
div.item-bloc {
div.item-row {
div.item-col:last-child {
justify-content: flex-start;
}
}
}
}
dl {
dd {
margin-left: 1em;
}
}
</style>

View File

@ -21,6 +21,8 @@ class ThirdPartyNormalizer implements NormalizerInterface, NormalizerAwareInterf
$data['id'] = $thirdParty->getId();
$data['address'] = $this->normalizer->normalize($thirdParty->getAddress(), $format,
[ 'address_rendering' => 'short' ]);
$data['phonenumber'] = $thirdParty->getTelephone();
$data['email'] = $thirdParty->getEmail();
return $data;
}

View File

@ -0,0 +1,43 @@
---
openapi: "3.0.0"
info:
version: "1.0.0"
title: "Chill api"
description: "Api documentation for chill. Currently, work in progress"
servers:
- url: "/api"
description: "Your current dev server"
paths:
/1.0/thirdparty/thirdparty.json:
get:
tags:
- thirdparty
summary: Return a list of all thirdparty items
responses:
200:
description: "ok"
401:
description: "Unauthorized"
/1.0/thirdparty/thirdparty/{id}.json:
get:
tags:
- thirdparty
summary: Return a thirdparty item by id
parameters:
- name: id
in: path
required: true
description: The thirdparty id
schema:
type: integer
format: integer
minimum: 1
responses:
200:
description: "ok"
404:
description: "not found"
401:
description: "Unauthorized"