[MicroApp 47] Micro Crm : exercice de crm en 2h30 avec AngularJs, ngMap et Bootstrap.

Introductioncrm-icon.png


Aujourd’hui je crée une micro Application CRM en 2h30 de temps ( de 15hà 17h20) en partant de zéro.

Fonctionnalités:

  • Ajout de nouveaux prospects par vendeurs.
  • On retrouve le prospect sur la carte par clic sur un bouton.
  • Notation des prospects et importance.

On pourrait ajouter :

  • Des statistiques graphiques.
  • Un back End NoSql Firebase ou en PHP ou Java.
  • Autocomplete Google sur la case du prospect !!!
  • Le dropdown des noms des vendeurs doit provenir d’une requête et pas être codé en dur bien sur

Libs utilisées :

  • Bootstrap 3.0 et css
  • AngularJs 1.5
  • Jquery (besoin pour faire tourner boostrap)
  • NgMap
  • Bower (Pour installer le tout très rapidement et à jour)

Testing en ligne : CLIQUER ICI

Photo de l’app :


microcrm.jpg

Explication Fonctionnelle :


 

L’admin attribue des prospects à ses Vendeurs.

Les vendeurs peuvent voir leurs prospects dédiés sur Google Map et les noter.

 

Explication Technique :


$scope.vdrs est le modèle de données des prospects(Contrairement à ce que le nom de la var pourrait laisser penser…), Chacun de ces objets Json prospects comprends le nom d’un vendeur, de cette façon, on peut filtrer ensuite les prospects de tel ou tel vendeur dans le développement. ce modèle ne comprenant que 3 champs, on peut aisément l’enregistrer en BDD relationelle en faisant un mapping objet.relationnel. Mais bien sur, sur Firebase c’est un milliard de fois mieux en noSql avec AngularFire et le 3 ways binding !

$scope.referentielVendeur est le référentiel des vendeurs avec leur age, leur durée de travail etc … Il est indépendant de $scope.vdrs.

 

Le code :


Le fichier JS qui contient le controleur AngularJS :

var app = angular.module('App', ['ngMap','ui.bootstrap'])

app.controller('MainCtrl', function($scope,$uibModal) {

	/* DONNES DE DEMONSTRAION */
	$scope.vdrs = [
	{"nom":"Dupuis","prospect":"renault 150 Avenue de la Division Leclerc, 95160 Montmorency","telephone":"03 89 09 09 09","note":"3","importance":"4"},
	{"nom":"Dubert","prospect":"Psa  Route de Chalampé, 68390 Sausheim","telephone":"03 89 09 09 09","note":"3","importance":"2"},
	{"nom":"Hammed","prospect":"Mercedes 116 Avenue Henri Barbusse, 92700 Colombes","telephone":"01 41 19 79 79","note":"3","importance":"1"},
	{"nom":"Dupuis","prospect":"Fiat 25 Avenue du Général de Gaulle, 92250 La Garenne-Colombes","telephone":"01 46 49 80 80","note":"3","importance":"2"}
	]

	$scope.referentielVendeur = [
	{"nom":"Dupuis","prenom":"Michel","age":"37","duree":"5"},
	{"nom":"Dubert","prenom":"Roger","age":"22","duree":"5"},
	{"nom":"Hammed","prenom":"Mohammed","age":"28","duree":"7"}
	]

	/* INITS */
	$scope.nouveauProspect = {};
	$scope.adresse="";
	$scope.adresseOk="Paris";
	$scope.liste = [];
	$scope.nomVendeur = "";
	$scope.vendeurs = [];

	/* Initialisation de la date et de l'heure du jour en cours */
	var date = new Date();
	var d = date.getDate();
    d = ("0" + d).slice(-2)
    var m = date.getMonth();
	m = (m+1);
    m = ("0" + m).slice(-2)
    var y = date.getFullYear();

	$scope.date  =  d+'-'+m+'-'+y;

	/* Cherche une adresse dans googlemap et l'ajoute à la liste*/
	$scope.chercherAdresse = function(adresse){
		if(adresse){
			var x = adresse;
			$scope.adresseOk = x;
		}
		else{
			var x = $scope.adresse;
			$scope.adresseOk = x;
			$scope.liste.push({"Adresse":x});
		}
	}

	/* Affecte la liste des villes à un vendeur */
	$scope.affecterListe = function(){
		var x = angular.copy($scope.nomVendeur);
		var z = angular.copy($scope.liste); // Angular Copy debinde le z de $scope.liste
		$scope.vendeurs.push({"nom":x,"liste":z});

	}

	/* Supprimer une adressse */
	$scope.s=function(index){
		$scope.liste.splice(index,1);
	}

	/* Lorsquon choisit un vendeur dans le dropown, la var change */
	$scope.setVendeur = function(vendeur){
		$scope.choixVendeur =  vendeur;

	}

	/* Sert à visualiser sur la carte */
	$scope.setAdresse = function(adresse){
		$scope.adresseOk = adresse;

	}

	/* Ajouter un prospect  */
	$scope.ajoutProspect = function (){
		$scope.vdrs.push({"nom":$scope.nouveauProspect.nom,"prospect":$scope.nouveauProspect.prospect,"telephone":$scope.nouveauProspect.telephone});
	}

});

Le code HTML :

<html ng-app="App">
	<head>
		<meta charset="utf-8" />
		<title>Exercice Google Maps.</title>

		<!-- JQUERY ET BOOTSRAP -->
		<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
		<script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
		<link rel="stylesheet" type="text/css" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">

		<!-- ANGULAR -->
		<script src='bower_components/angular/angular.min.js'></script>

		<!-- GOOGLE MAP ET NGMAP -->
		<script src="https://maps.google.com/maps/api/js?libraries=placeses,visualization,drawing,geometry,places"></script>
		<script src="bower_components/ngmap/build/scripts/ng-map.min.js"></script>

		<!-- UI BOOTSTRAP -->
		<script src='bower_components/angular-bootstrap/ui-bootstrap.min.js'></script>
		<script src='bower_components/angular-bootstrap/ui-bootstrap-tpls.min.js'></script>
	</head>

<body ng-controller="MainCtrl">
	<div class="container-fluid">
		<nav class="navbar navbar-inverse">
		  <div class="container-fluid">
	<!-- Brand and toggle get grouped for better mobile display -->
				<div class="navbar-header">
				  <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
					<span class="sr-only">Toggle navigation</span>
					<span class="icon-bar"></span>
					<span class="icon-bar"></span>
					<span class="icon-bar"></span>
				  </button>
				  <a class="navbar-brand" href="#">MicroCrm</a>
			</div>

	<!-- Collect the nav links, forms, and other content for toggling -->
		<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
		  <ul class="nav navbar-nav">
		  <li class="dropdown">
			  <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Vendeur <span class="caret"></span></a>
			  <ul class="dropdown-menu">
				<li><a ng-click = "setVendeur('Dupuis')" href="#">Dupuis</a></li>
				<li><a ng-click = "setVendeur('Dubert')" href="#">Dubert</a></li>
				<li><a ng-click = "setVendeur('Hammed')" href="#">Hammed</a></li>
				<li role="separator" class="divider"></li>
				<li><a href="#">Stats</a></li>
				<li role="separator" class="divider"></li>
				<li><a href="#">Contacts</a></li>
			  </ul>
			</li>

		  </ul>
		  <form class="navbar-form navbar-left" role="search">
			<div class="form-group">
			  <input type="text" class="form-control" ng-model="chercheProspect" placeholder="Chercher un prospect">
			</div>
		   <!--  <button type="submit" class="btn btn-default">Rechercher</button> -->
			</form>
		  <ul class="nav navbar-nav navbar-right">

		   <div class="form-group">
			<form>
			  <input type="text" class="form-control" ng-model="nouveauProspect.prospect" placeholder="Ajouter un prospect" required>
			  <input type="text" class="form-control" ng-model="nouveauProspect.telephone" placeholder="Téléphone du Prospect" required>
				<select class="form-control" ng-model="nouveauProspect.nom" required>
				<option value="">Vendeur</option>
				<option value="Dupuis">Dupuis</option>
				<option value="Dupert">Dupert</option>
				<option value="Hammed">Hammed</option>
				</select>
				<button type="submit" ng-click="ajoutProspect()" class="btn btn-default">Ajouter</button>
			</form>
			</div>

		  </ul>
			</div><!-- /.navbar-collapse -->
		</div><!-- /.container-fluid -->
	</nav>

<div class="row" style="min-width:300px;">
	<div class="col-md-4"><img src="img-app/vendeur.png" class="img"></src>
		<table  class="table" >
			<thead>
			  <tr>

				<th>Nom: </th>
				<th >Prénom : </th>
				<th >Age :</th>
				<th >Durée de Travail:</th>
				</tr>
			</thead>
			<tbody>
				<tr ng-repeat="x in referentielVendeur |filter:choixVendeur">
					<td>{{::x.nom}}</td>
					<td>{{::x.prenom}}</td>
					<td>{{::x.age}}</td>
					<td>{{::x.duree}}</td>
				</tr>
			</tbody>
		</table>
	</div>
	<div class="col-md-8">
		<table  class="table" >
			<thead>
			  <tr>

				<th>Prospect</th>
				<th >Téléphone: </th>
				<th >Note:</th>
				<th >Importance:</th>
				</tr>
			</thead>
			<tbody>
				<tr ng-repeat="d in vdrs |filter:choixVendeur|filter:chercheProspect">
					<td><button class="btn btn-primary" ng-click="setAdresse(d.prospect)">{{::d.prospect}}</button></td>
					<td>{{::d.telephone}}</td>
					<td>
					<uib-rating ng-model="d.note" max="5" ></uib-rating>
					</td>
					<td><uib-rating ng-model="d.importance" max="5" ></uib-rating></td>
				</tr>
			</tbody>
		</table>
	</div>
</div>

	<div>
		<ng-map style="height:100%" center="{{adresseOk}}" zoom="14">
		<marker ng-repeat="d in vdrs |filter:choixVendeur" position="{{d.prospect}}" ></marker>
		</ng-map>
	</div> 

	<!-- FIND DE DIV GLOBALE FLUID BOOSTRAP POUR LES PHONES -->
</div>
<script src="app.js"> </script>
</body>

<style>
.angular-google-map-container { height: 600px; }
.div{
border-style: groove;
}
.img{
width:100px;
height:100px;
}
</style>
</html>

Publicités