Skip to content

Instantly share code, notes, and snippets.

@jeromenerf
Last active November 28, 2017 15:22
Show Gist options
  • Save jeromenerf/b770b976b605cb42121be8fd5fed9870 to your computer and use it in GitHub Desktop.
Save jeromenerf/b770b976b605cb42121be8fd5fed9870 to your computer and use it in GitHub Desktop.
import $ from "jquery";
import React, { Component } from "react";
import { render } from "react-dom";
import Form from "react-jsonschema-form";
import * as schemas from "./schemas";
import DatePicker from "react-datepicker";
import moment from "moment";
import "moment/locale/fr";
export class AvisForm extends React.Component {
constructor(props) {
super(props);
this.state = {
data: props.initialData
};
this.onSubmit = this.onSubmit.bind(this);
}
prepareSchema() {
var defs = schemas.GetModels(this.props.defaults);
return Object.assign(
Object.assign({}, defs),
defs.definitions[this.props.model]
);
}
prepareUiSchema() {
return Object.assign(
Object.assign({}, schemas.ui),
schemas.ui.definitions[this.props.model]
);
}
onError() {
console.log.bind(console);
}
onSubmit(url) {
var _this = this;
var f = data => {
var payload = data.formData;
$.ajax({
type: "PUT",
url: url,
data: JSON.stringify(payload),
success: function(response, status, jxhr) {
// Set state to the JSON response from the API, could be an array or an object
_this.setState({ data: response });
_this.forceUpdate();
},
contentType: "application/json",
dataType: "json"
});
};
return f;
}
render() {
return (
<Form
schema={this.prepareSchema()}
formData={this.state.data}
fields={this.props.fields}
formContext={this.props.defaults}
uiSchema={this.prepareUiSchema()}
onSubmit={this.onSubmit(this.props.submitUrl)}
onError={this.onError}
>
<div>
<input type="submit" className="btn btn-success" />
<a href={window.location} className="btn btn-warning">
Annuler
</a>
</div>
</Form>
);
}
}
// NotiphyDatePicker is a custom widget to handle the datetime issues with mozilla-react-form
export class NotiphyDatePicker extends React.Component {
constructor(props) {
super(props);
this.state = {
date: props.formData
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(newdate) {
this.setState(Object.assign(this.state, { date: newdate.format() }));
this.props.onChange(this.state.date);
}
render() {
return (
<div>
<label className="form-label">{this.props.schema.title}</label>
<DatePicker
selected={moment(this.state.date)}
onChange={this.handleChange}
showTimeSelect
timeFormat="HH:mm"
timeIntervals={15}
dateFormat="LLLL"
locale="fr"
className="form-control"
/>
</div>
);
}
}
// AutocompleteProducts is a custom widget to provide a search box and a select list to easily get products
export class AutocompleteProducts extends React.Component {
constructor(props) {
super(props);
this.state = {
pattern: "",
productid: props.formData,
products: props.formContext.Products.filter(product => {
return product.ID == props.formData;
})
};
this.getProducts = this.getProducts.bind(this);
this.handleChange = this.handleChange.bind(this);
this.handleInput = this.handleInput.bind(this);
console.log(props.formContext);
}
getProducts() {
var _this = this;
// If no pattern, set state to actual selected product
if (this.state.pattern.length == 0) {
_this.setState(
Object.assign(this.state, {
products: _this.props.formContext.Products.filter(product => {
return product.ID == _this.props.formData;
})
})
);
}
if (this.state.pattern.length > 2) {
// Get products matching pattern
$.getJSON(
`/autocomplete/products?pattern=${this.state.pattern}`,
function(data) {
if (data.length > 0) {
// Update state with autocompleted products and set a default value
_this.setState(
Object.assign(_this.state, {
products: data,
productid: data[0].ID
})
);
// Update form state, not optimal
_this.props.onChange(_this.state.productid);
}
}
);
}
}
handleInput(event) {
this.setState(Object.assign(this.state, { pattern: event.target.value }));
this.getProducts();
}
handleChange(event) {
this.setState(
Object.assign(this.state, { productid: Number(event.target.value) })
);
this.props.onChange(this.state.productid);
}
componentDidMount() {
this.getProducts();
}
render() {
return (
<div>
<label className="control-label">Produit</label>
<div className="form-group row">
<div className="col col-md-3">
<input
className="form-control"
type="text"
placeholder="Chercher un produit"
value={this.state.pattern}
onChange={this.handleInput}
/>
</div>
<div className="col col-md-9">
<select
name="ProductID"
className="form-control"
value={this.state.productid}
onChange={this.handleChange}
>
{this.state.products.map(function(product, i) {
return (
<option key={i} value={product.ID}>
{product.Name} ({product.Substances} - DRE: {product.REI}h)
</option>
);
})}
</select>
</div>
</div>
</div>
);
}
}
import $ from "jquery";
import React, { Component } from "react";
import { render } from "react-dom";
import Form from "react-jsonschema-form";
import { AvisForm, AutocompleteProducts, NotiphyDatePicker } from "./components/avisform";
import { Tab, Tabs, TabList, TabPanel } from "react-tabs";
var avisform = document.getElementById("avisform"),
initialData = JSON.parse(avisform.dataset.initialdata),
interventionid = initialData.ID,
contract = [],
contractreq = $.getJSON(`/api/contracts/${initialData.ContractID}`, function(
data
) {
contract = data;
}),
defaults = {};
$.when(contractreq).done(function() {
var users = contract.Users.filter(user => {
return contract.ContractUsers.find(cu => {
return ((cu.Role == "MANAGER" || cu.Role == "WORKER") && cu.UserID == user.ID)
})
});
defaults = {
InterventionID: interventionid,
Users: users,
Parcels: contract.Parcels,
Products: initialData.Products ? initialData.Products : []
};
render(
<div>
<Tabs>
<TabList>
<Tab>Général</Tab>
<Tab>Produits</Tab>
<Tab>Personnes</Tab>
<Tab>Parcelles</Tab>
</TabList>
<TabPanel>
<AvisForm
model="Intervention"
submitUrl={`/api/contracts/${contract.ID}/interventions`}
defaults={defaults}
fields={{
notiphydatepicker: NotiphyDatePicker
}}
initialData={initialData}
/>
</TabPanel>
<TabPanel>
<AvisForm
model="InterventionProducts"
submitUrl={`/api/interventions/${interventionid}/interventionproducts`}
defaults={defaults}
fields={{
autoprod: AutocompleteProducts
}}
formContext={defaults}
initialData={initialData.InterventionProducts}
/>
</TabPanel>
<TabPanel>
<AvisForm
model="InterventionUsers"
submitUrl={`/api/interventions/${interventionid}/interventionusers`}
defaults={defaults}
initialData={initialData.InterventionUsers}
/>
</TabPanel>
<TabPanel>
<AvisForm
model="InterventionParcels"
submitUrl={`/api/interventions/${interventionid}/interventionparcels`}
defaults={defaults}
initialData={initialData.InterventionParcels}
/>
</TabPanel>
</Tabs>
</div>,
document.getElementById("avisform")
);
});
// now returns the current date formatted as an iso format string
function now() {
var d = new Date();
return d.toISOString();
}
// GetModels returns the form models with the provided defaults
export function GetModels(defaults) {
var getdefault = function(key, fallback) {
var val = fallback;
if (defaults !== undefined && defaults.hasOwnProperty(key)) {
val = defaults[key];
}
return val;
},
getdefaultenum = function(key, fallback, allowempty) {
var val = fallback;
if (defaults !== undefined && defaults.hasOwnProperty(key)) {
if (defaults[key] != null) {
val = defaults[key].map(function(k) {
return k.ID;
});
}
}
if (allowempty) {
val.unshift(0);
}
return val;
},
getdefaultenumnames = function(key, fallback, allowempty) {
var val = fallback;
if (defaults !== undefined && defaults.hasOwnProperty(key)) {
if (defaults[key] != null) {
val = defaults[key].map(function(k) {
return [
k.Name,
k.Names,
k.Substances,
k.LastName,
k.FirstName,
k.Variety,
k.REI
].join(" ");
});
}
}
if (allowempty) {
val.unshift("--");
}
return val;
};
return {
definitions: {
Contract: {
title: "Contrat",
type: "object",
required: ["Name"],
properties: {
Name: {
type: "string",
title: "Nom",
default: ""
},
Signature: {
type: "string",
title: "Signature",
default: ""
},
MinREI: {
type: "number",
title: "DRE minimale",
default: 0,
},
}
},
Product: {
title: "Produit phytopharmaceutique",
type: "object",
required: ["Name"],
properties: {
AMM: {
type: "string",
title: "AMM",
default: "Numéro AMM..."
},
Name: {
type: "string",
title: "Nom",
default: "KiLLEx 12000 ..."
},
Names: {
type: "string",
title: "Autres noms",
default: "Fongicide ..."
},
Substances: {
type: "string",
title: "Substances",
default: "..."
},
Functions: {
type: "string",
title: "Fonctions",
default: "Fongicide ..."
},
Description: {
type: "string",
title: "Description",
default: "Conditions d'emploi, usages, etc"
},
REI: {
type: "number",
title: "Période de ré-entrée",
default: "24"
}
}
},
User: {
title: "Personne",
type: "object",
required: ["LastName", "Email", "UserName"],
properties: {
UserName: {
type: "string",
title: "Login",
default: ""
},
LastName: {
type: "string",
title: "Nom",
default: ""
},
FirstName: {
type: "string",
title: "Prénom",
default: ""
},
Email: {
type: "string",
title: "Email",
default: ""
},
Phone: {
type: "string",
title: "Téléphone",
default: ""
},
Role: {
type: "string",
title: "Rôle global",
default: "PUBLIC"
},
Street: {
type: "string",
title: "Rue",
default: ""
},
StreetComplement: {
type: "string",
title: "Rue (Complément)",
default: ""
},
ZipCode: {
type: "string",
title: "Code Postal",
default: ""
},
City: {
type: "string",
title: "Ville",
default: ""
},
CityComplement: {
type: "string",
title: "Ville (Complément)",
default: ""
},
Country: {
type: "string",
title: "Pays",
default: "France"
},
// IsAdmin: {
// type: "boolean",
// title: "Admin ?",
// default: false
// },
Geom: {
type: "object",
title: "Position",
required: ["Lat", "Lng"],
properties: {
Lng: {
type: "number",
title: "Longitude",
default: navigator.geolocation.getCurrentPosition(function(
position
) {
return position.coords.longitude;
})
},
Lat: {
type: "number",
title: "Latitude",
default: navigator.geolocation.getCurrentPosition(function(
position
) {
return position.coords.latitude;
})
}
}
},
ClearPassword: {
type: "string",
title: "Mot de passe",
default: ""
}
}
},
Parcel: {
title: "Parcelle",
type: "object",
required: ["Name"],
properties: {
Name: {
type: "string",
title: "Nom",
default: ""
},
ContractID: {
type: "number",
Title: "Contrat",
default: getdefault("ContractID", 0)
},
Municipality: {
type: "string",
title: "Commune",
default: ""
},
Area: {
type: "number",
title: "Surface (ha)",
default: 1.5
},
Variety: {
type: "string",
title: "Variété",
default: ""
},
Tag: {
type: "string",
title: "Mot clé",
default: "defaut"
},
Geom: {
type: "object",
title: "Position",
required: ["Lat", "Lng"],
properties: {
Lat: {
type: "number",
title: "Latitude",
default: 0
},
Lng: {
type: "number",
title: "Longitude",
default: 0
}
}
}
}
},
Intervention: {
title: "Intervention",
type: "object",
required: ["Name"],
properties: {
Name: {
type: "string",
title: "Nom",
default: ""
},
DueDate: {
type: "string",
format: "date-time",
title: "Date prévue",
default: now()
},
Kind: {
type: "string",
title: "Type d'intervention",
enum: [
"traitementphyto",
"traitementherbicide",
"rognage",
"eclaircissagechimique",
"effeuillagemecanise",
"epampragechimique",
"epampragemecanique",
"relevagemecanique",
"travaildusol",
"vendangesmecaniques"
],
enumNames: [
"Traitement phyto",
"Traitement herbicide",
"Rognage",
"Eclaircissage chimique",
"Effeuillage mécanisé",
"Epamprage chimique",
"Epamprage mécanique",
"Relevage mécanique",
"Travail du sol",
"Vendanges mécaniques"
],
default: "traitementphyto"
},
Description: {
type: "string",
title: "Description",
default: ""
},
PublicMessage: {
type: "string",
title: "Message pour le public",
default: ""
},
Status: {
type: "string",
title: "Statut",
enum: ["NEW", "TODO", "DOING", "DONE", "CANCELLED"],
enumNames: [
"BROUILLON",
"A FAIRE",
"EN COURS",
"RÉALISÉE",
"ANNULÉE"
],
default: "NEW"
},
MinREI: {
type: "number",
title: "DRE minimale",
default: 0,
},
Progress: {
type: "number",
title: "Avancement",
default: 0,
minimum: 0,
maximum: 100,
},
DoneDate: {
type: "string",
format: "date-time",
title: "Date de fin constatée"
}
}
},
ContractUser: {
type: "object",
title: "Personne associée",
properties: {
UserID: {
type: "number",
title: "ID personne",
enum: getdefaultenum("Users", [], true),
enumNames: getdefaultenumnames("Users", [], true)
},
ContractID: {
type: "number",
title: "ID contrat",
default: getdefault("ContractID", 0)
},
Role: {
type: "string",
title: "Rôle",
enum: ["MANAGER", "WORKER", "PRIVATE", "PUBLIC"],
enumNames: ["Manager", "Intervenant", "Privé", "Public"],
default: "WORKER"
}
}
},
UserContract: {
type: "object",
title: "Personne associée",
properties: {
UserID: {
type: "number",
title: "ID personne",
default: getdefault("UserID", 0)
},
ContractID: {
type: "number",
title: "ID contrat",
default: getdefault("ContractID", 0),
enum: getdefaultenum("Contracts", [], true),
enumNames: getdefaultenumnames("Contracts", [], true)
},
Role: {
type: "string",
title: "Rôle",
enum: ["MANAGER", "WORKER", "PRIVATE", "PUBLIC"],
enumNames: ["Manager", "Intervenant", "Privé", "Public"],
default: "WORKER"
}
}
},
InterventionUser: {
type: "object",
title: "Personne associée",
properties: {
UserID: {
type: "number",
title: "ID personne",
enum: getdefaultenum("Users", [], true),
enumNames: getdefaultenumnames("Users", [], true)
},
InterventionID: {
type: "number",
title: "ID intervention",
default: getdefault("InterventionID", 0)
}
}
},
InterventionUserLight: {
type: "object",
properties: {
UserID: {
type: "number",
title: " ",
enum: getdefaultenum("Users", [], true),
enumNames: getdefaultenumnames("Users", [], true)
},
InterventionID: {
type: "number",
default: getdefault("InterventionID", 0)
}
}
},
InterventionParcel: {
type: "object",
// title: "Parcelle associée",
properties: {
ParcelID: {
type: "number",
title: "Parcelle",
enum: getdefaultenum("Parcels", [], true),
enumNames: getdefaultenumnames("Parcels", [], true)
},
InterventionID: {
type: "number",
title: "ID intervention",
default: getdefault("InterventionID", 0)
},
Status: {
type: "string",
title: "Statut",
enum: ["TODO", "DOING", "DONE", "CANCELLED"],
enumNames: ["Prévue", "En cours", "Réalisée", "Annulée"],
default: "TODO"
},
UserID: {
type: "number",
title: "ID utilisateur",
enum: getdefaultenum("Users", [], true),
enumNames: getdefaultenumnames("Users", [], true),
default: 0
},
Message: {
type: "string",
title: "Message",
default: ""
}
}
},
ParcelUser: {
type: "object",
title: "Personne associé",
properties: {
ParcelID: {
type: "number",
title: "ID parcelle",
enum: getdefaultenum("Parcels", [], true),
enumNames: getdefaultenumnames("Parcels", [], true)
},
UserID: {
type: "number",
title: "ID utilisateur",
enum: getdefaultenum("Users", [], true),
enumNames: getdefaultenumnames("Users", [], true),
default: 0
},
Role: {
type: "string",
title: "Role",
enum: ["MANAGER", "WORKER", "PRIVATE", "PUBLIC"],
enumNames: ["Manager", "Intervenant", "Privé", "Public"],
default: "PRIVATE"
}
}
},
InterventionProduct: {
type: "object",
title: "Produit associé",
properties: {
ProductID: {
type: "number",
title: "ID produit"
// enum: getdefaultenum("Products", []),
// enumNames: getdefaultenumnames("Products", [])
},
InterventionID: {
type: "number",
title: "ID intervention",
default: getdefault("InterventionID", 0)
},
REI: {
type: "number",
title: "DRE alternative",
default: 0,
},
Dose: {
type: "number",
title: "Dose",
default: 100,
min: 0,
max: 200,
step: 10,
},
}
},
UserContracts: {
type: "array",
title: "Contrats associés",
items: {
$ref: "#/definitions/UserContract"
}
},
ContractUsers: {
type: "array",
title: "Personnes associées",
items: {
$ref: "#/definitions/ContractUser"
}
},
ParcelUsers: {
type: "array",
title: "Personnes associées",
items: {
$ref: "#/definitions/ParcelUser"
}
},
Parcels: {
type: "array",
title: "Parcelles",
items: {
$ref: "#/definitions/Parcel"
}
},
InterventionParcels: {
type: "array",
title: "Parcelles associées",
items: {
$ref: "#/definitions/InterventionParcel"
}
},
ParcelInterventions: {
type: "array",
title: "Interventions associées",
items: {
$ref: "#/definitions/InterventionParcel"
}
},
InterventionUsers: {
type: "array",
title: "Personnes associées",
items: {
$ref: "#/definitions/InterventionUserLight"
}
},
UserInterventions: {
type: "array",
title: "Interventions associées",
items: {
$ref: "#/definitions/InterventionUser"
}
},
InterventionProducts: {
type: "array",
title: "Produits associés",
items: {
$ref: "#/definitions/InterventionProduct"
}
},
ProductInterventions: {
type: "array",
title: "Interventions associées",
items: {
$ref: "#/definitions/InterventionProduct"
}
}
}
};
}
// ui is the presentation settings object
export const ui = {
definitions: {
Contract: {
Name: {
"ui:placeholder": "Domaine de ...",
"ui:help": "Le nom sera utilisé pour les notifications"
},
Signature: {
"ui:placeholder": "Ce message vous est adressé de la part de ...",
"ui:help": "La signature sera utilisée pour les notifications"
},
MinREI: {
"ui:placeholder": "0",
"ui:help":
"Si une DRE minimale est définie pour votre exploitation, renseignez là ici."
}
},
Intervention: {
Name: {
"ui:help":
"Le nom de l'intervention. Par défaut, le nom est l'identifiant de l'intervention"
},
DueDate: {
// "ui:widget": "alt-datetime",
"ui:field": "notiphydatepicker",
"ui:help":
"La date souhaitée pour l'intervention, vous pouvez spécifier l'heure"
},
DoneDate: {
// "ui:widget": "alt-datetime",
"ui:field": "notiphydatepicker",
"ui:help":
"La date de fin de l'intervention, rensignée automatiquement lorsque toutes les parcelles sont terminées"
},
Description: {
"ui:placeholder": "Explication si nécessaire ...",
"ui:help":
"La description est utile pour les utilisateurs du contrat. Elle n'est pas communiquée au public"
},
PublicMessage: {
"ui:placeholder": "Explication si nécessaire ...",
"ui:help":
"Le message public est réservé aux notifications envoyées au public"
},
MinREI: {
"ui:placeholder": "0",
"ui:help":
"La DRE minimale pour cette intervention. (Par défaut, celle de votre contrat)."
},
Progress: {
"ui:widget": "range",
"ui:disabled": true
},
Status: {
// "ui:disabled": true
}
},
InterventionProducts: {
"ui:options": {
orderable: false
},
items: {
InterventionID: {
"ui:widget": "hidden"
},
ProductID: {
"ui:field": "autoprod",
"ui:help":
"Cherchez votre PPP en commençant à saisir son nom, 3 lettres minimum. Si vous ne trouvez pas, vous pouvez utiliser un PPP générique, en cherchant le terme \"géné...\""
},
REI: {
"ui:placeholder": "0",
"ui:help":
"Fournissez une DRE alternative, obligatoirement supérieure, pour le PPP si besoin (erreur, produit générique ou manquant, réglementation locale ...)."
},
Dose: {
"ui:widget": "updown",
"ui:help": "La dose est exprimée en pourcentage de la dose standard."
}
}
},
InterventionUsers: {
"ui:options": {
orderable: false
},
items: {
InterventionID: {
"ui:widget": "hidden"
}
}
},
InterventionParcel: {
ParcelID: {
"ui:disabled": true
},
InterventionID: {
"ui:widget": "hidden"
},
Message: {
"ui:widget": "textarea",
"ui:placeholder": "Explication si nécessaire ...",
"ui:help": "La dose est exprimée en pourcentage de la dose standard."
},
UserID: {
"ui:widget": "hidden"
}
},
InterventionParcels: {
"ui:options": {
orderable: false
},
items: {
InterventionID: {
"ui:widget": "hidden"
},
Message: {
"ui:widget": "textarea",
"ui:placeholder": "Explication si nécessaire ..."
},
UserID: {
"ui:widget": "hidden"
}
}
},
UserContracts: {
"ui:options": {
orderable: false
},
items: {
UserID: {
"ui:widget": "hidden"
}
// ContractID: {
// "ui:disabled": true
// }
}
},
ContractUsers: {
"ui:options": {
orderable: false
},
items: {
// UserID: {
// "ui:disabled": true
// },
ContractID: {
"ui:widget": "hidden"
}
}
},
Parcels: {
"ui:options": {
orderable: false
},
items: {
ContractID: {
"ui:disabled": true
}
}
},
Parcel: {
ContractID: {
"ui:disabled": true
}
}
},
Password: {
"ui:widget": "password"
},
Description: {
"ui:widget": "textarea",
"ui:placeholder": "Description longue ..."
},
ID: {
"ui:readonly": true
},
CreatedAt: {
"ui:readonly": true
},
UpdatedAt: {
"ui:readonly": true
},
items: {
ID: {
"ui:widget": "hidden"
},
InterventionID: {
"ui:disabled": true
},
CreatedAt: {
"ui:disabled": true
},
UpdatedAt: {
"ui:disabled": true
},
"ui:options": {
orderable: false
}
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment