JSFiddle - React, Tailwind, and code Playground
by Hugo Carneiro
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/typeahead.js/0.11.1/typeahead.bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js"></script>
<div class="modal fade overlay overlay-sm" id="invite-modal" tabindex="-1">
<div class="modal-dialog modal-md">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<i class="fa fa-times-thin fa-2x"></i>
</button>
<h3 class="modal-title" id="myModalLabel">Who do you collaborate with?</h3>
</div>
<div class="modal-body">
<h5>Get more done with your team or friends.</h5>
<p>Collaborators will have full access to edit the app.</p>
<div class="search-team-holder">
<i class="fa fa-search search-icon"></i>
<input
type="text"
class="form-control search-team"
placeholder="Search team members or add new users"
/>
<p>Enter a name or an email and press enter</p>
</div>
<div class="users-list-holder new-users hidden">
<div class="team-search-results-holder">
<!-- Users here -->
</div>
</div>
<div class="buttons-holder">
<div class="btn btn-primary btn-invite disabled">Invite</div>
</div>
</div>
</div>
</div>
</div>
CSS
/* Studio styles */
body {
color: #333;
}
a,
a:hover,
a:focus,
a:active {
color: #00abd1;
}
.btn {
display: inline-block;
margin-bottom: 0;
font-weight: 400;
text-align: center;
vertical-align: middle;
touch-action: manipulation;
cursor: pointer;
background-image: none;
border: 1px solid transparent;
white-space: nowrap;
padding: 9px 12px;
font-size: 14px;
line-height: 1.42857;
border-radius: 4px;
user-select: none;
}
.btn-primary,
.btn-primary.focus,
.btn-primary:focus {
color: #fff;
background-color: #00abd1;
}
.btn-primary.active,
.btn-primary:active,
.btn-primary:hover,
.open > .btn-primary.dropdown-toggle {
color: #fff;
border: 1px solid transparent;
background-color: #00819e;
}
.btn-default,
.btn-default.focus,
.btn-default:focus {
color: #474974;
background-color: #eae9ec;
}
.btn-default.active,
.btn-default:active,
.btn-default:hover,
.open > .btn-default.dropdown-toggle {
color: #474974;
border: 1px solid transparent;
background-color: #d0ced4;
}
.form-control {
padding: 9px 12px;
height: 40px;
}
/* Modal styles */
.modal#invite-modal .modal-dialog {
margin: calc(5.2rem + 20px) 20px 20px;
transform: translate(0px, 0px);
}
.modal#invite-modal .modal-content {
box-shadow: none;
border: none;
border-radius: 0px;
}
.modal#invite-modal .modal-header {
position: relative;
display: flex;
align-items: center;
justify-content: center;
height: 65px;
line-height: 65px;
border-color: rgb(227, 233, 235);
}
.modal#invite-modal .modal-header h3 {
margin-top: 10px;
margin-bottom: 10px;
font-weight: 700;
line-height: 1.1;
color: #474974;
font-size: 24px;
}
.modal#invite-modal .modal-header .close {
margin-top: 0px;
position: absolute;
top: 0px;
right: 0px;
left: auto;
width: 65px;
height: 65px;
border-radius: 0px;
text-align: center;
line-height: 1.714;
font-size: 1.4rem;
z-index: 1;
pointer-events: all;
color: rgb(153, 153, 153);
...
JavaScript
var API_URL = {
eu: 'https://api.fliplet.com',
us: 'https://us.api.fliplet.com',
ca: 'https://ca.api.fliplet.com'
};
var organizations = window.flOrganizations || [{}];
var organizationUsers = organizations[0].users || [];
var currentApp = window.flUser && window.flUser.currentApp || {};
var currentUser = window.flUser && window.flUser.data || {
fullName: "Hugo Carneiro",
id: 123,
firstName: "Hugo",
lastName: "Carneiro",
email: "[email protected]",
createdAt: "2018-03-28T16:07:52.490Z",
userRoleId: 1
};
var appUsers = currentApp.users;
var allOrganizationUsers = [];
var existingUsers = [];
var selectedTesters = [];
var userSearchKeys = [
'id',
'firstName',
'lastName',
'email'
];
const appRoles = [{
id: 1,
name: 'publisher',
validRoles: [1, 2, 4]
},
{
id: 2,
name: 'editor',
validRoles: [2, 4],
inviteTabRoles: [1, 2]
},
{
id: 4,
name: 'tester',
validRoles: [4],
inviteTabRoles: [4]
}
];
var roleIds = {
tester: [4],
editor: [2],
publisher: [2, 1]
};
var tabContext = 'editor';
var usersLimit = 3;
var ExperimentInvite = {
isValidEmail: function (email) {
return /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/i.test(email);
},
suggestionTemplate: function (data) {
return '<div class="user-info-holder"><p>' + data.fullName + '</p><p>' + data.email + '</p></div>';
},
notFoundTemplate: function (data) {
if (ExperimentInvite.isValidEmail(data.query)) {
return '<div class="tt-no-results">No team members found for: <strong>' + data.query + '</strong><br>Press enter to add it now.</div>';;
}
return '<div class="tt-no-results">Sorry, no results found for: <strong>' + data.query + '</strong>.<br>Enter an email instead.</div>';
},
getOrganizationUsers: function () {
return new Promise(function (resolve, reject) {
$.ajax({
url:...