mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Get group admin page working with angular and new style
This commit is contained in:
parent
c542116b41
commit
8525bf4a72
7 changed files with 210 additions and 115 deletions
|
@ -231,7 +231,7 @@ block content
|
||||||
.row.row-spaced
|
.row.row-spaced
|
||||||
.col-md-12
|
.col-md-12
|
||||||
.card.card-thin
|
.card.card-thin
|
||||||
ul.list-unstyled.project-list(
|
ul.list-unstyled.project-list.structured-list(
|
||||||
select-all-list,
|
select-all-list,
|
||||||
ng-if="projects.length > 0",
|
ng-if="projects.length > 0",
|
||||||
ng-cloak
|
ng-cloak
|
||||||
|
@ -243,11 +243,11 @@ block content
|
||||||
select-all,
|
select-all,
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
)
|
)
|
||||||
span.title TITLE
|
| TITLE
|
||||||
.col-md-2
|
.col-md-2
|
||||||
span.owner OWNER
|
| OWNER
|
||||||
.col-md-4
|
.col-md-4
|
||||||
span.last-modified LAST MODIFIED
|
| LAST MODIFIED
|
||||||
li.project_entry.container-fluid(
|
li.project_entry.container-fluid(
|
||||||
ng-repeat="project in visibleProjects | orderBy:'lastUpdated':true",
|
ng-repeat="project in visibleProjects | orderBy:'lastUpdated':true",
|
||||||
ng-controller="ProjectListItemController"
|
ng-controller="ProjectListItemController"
|
||||||
|
|
|
@ -1,52 +1,84 @@
|
||||||
extends ../layout
|
extends ../layout
|
||||||
|
|
||||||
block content
|
block content
|
||||||
.container.box
|
.content.content-alt
|
||||||
.row
|
.container
|
||||||
.span12
|
.row
|
||||||
.page-header
|
.col-md-10.col-md-offset-1
|
||||||
h2 Group Admin
|
.card(ng-controller="GroupMembersController")
|
||||||
|
.page-header
|
||||||
|
.pull-right(ng-cloak)
|
||||||
|
small(ng-show="selectedUsers.length == 0") You have added <strong>{{ users.length }}</strong> of <strong>{{ groupSize }}</strong> available members
|
||||||
|
a.btn.btn-danger(
|
||||||
|
href,
|
||||||
|
ng-show="selectedUsers.length > 0"
|
||||||
|
ng-click="removeMembers()"
|
||||||
|
) Remove from group
|
||||||
|
h1 Group Account
|
||||||
|
|
||||||
div You are allowed up to
|
.row-spaced-small
|
||||||
strong #{subscription.membersLimit}
|
ul.list-unstyled.structured-list(
|
||||||
| members in this group
|
select-all-list,
|
||||||
|
ng-cloak
|
||||||
|
)
|
||||||
|
li.container-fluid
|
||||||
|
.row
|
||||||
|
.col-md-5
|
||||||
|
input.select-all(
|
||||||
|
select-all,
|
||||||
|
type="checkbox"
|
||||||
|
)
|
||||||
|
span.email EMAIL
|
||||||
|
.col-md-5
|
||||||
|
span.name NAME
|
||||||
|
.col-md-2
|
||||||
|
span.registered REGISTERED
|
||||||
|
li.container-fluid(
|
||||||
|
ng-repeat="user in users | orderBy:'email':true",
|
||||||
|
ng-controller="GroupMemberListItemController"
|
||||||
|
)
|
||||||
|
.row
|
||||||
|
.col-md-5
|
||||||
|
input.select-item(
|
||||||
|
select-individual,
|
||||||
|
type="checkbox",
|
||||||
|
ng-model="user.selected"
|
||||||
|
)
|
||||||
|
span.email {{ user.email }}
|
||||||
|
.col-md-5
|
||||||
|
span.name {{ user.first_name }} {{ user.last_name }}
|
||||||
|
.col-md-2
|
||||||
|
span.registered
|
||||||
|
i.fa.fa-check.text-success(ng-show="!user.holdingAccount")
|
||||||
|
i.fa.fa-times(ng-show="user.holdingAccount")
|
||||||
|
li(
|
||||||
|
ng-if="users.length == 0",
|
||||||
|
ng-cloak
|
||||||
|
)
|
||||||
|
.row
|
||||||
|
.col-md-12.text-centered
|
||||||
|
small No members
|
||||||
|
|
||||||
table.table-striped.table.table-striped
|
div(ng-if="users.length < groupSize", ng-cloak)
|
||||||
thead
|
hr
|
||||||
tr
|
p
|
||||||
th
|
.small Add more members
|
||||||
input(type="checkbox").select-all
|
form.form
|
||||||
th email
|
.row
|
||||||
th Name
|
.col-xs-6
|
||||||
th Registered
|
input.form-control(
|
||||||
|
name="email",
|
||||||
|
type="text",
|
||||||
|
placeholder="jane@example.com, joe@example.com",
|
||||||
|
ng-model="inputs.emails",
|
||||||
|
on-enter="addMembers()"
|
||||||
|
)
|
||||||
|
.col-xs-6
|
||||||
|
button.btn.btn-primary(ng-click="addMembers()") Add
|
||||||
|
|
||||||
tbody#userList
|
script(type="text/javascript").
|
||||||
-each user in users
|
window.users = !{JSON.stringify(users)};
|
||||||
tr
|
window.groupSize = #{subscription.membersLimit};
|
||||||
td
|
|
||||||
input(type="checkbox").select-one
|
|
||||||
td #{user.email}
|
|
||||||
td #{user.first_name} #{user.last_name}
|
|
||||||
td #{!user.holdingAccount}
|
|
||||||
td
|
|
||||||
input(type="hidden", name="user_id", value=user._id).user_id {{user._id}}
|
|
||||||
|
|
||||||
|
|
||||||
div
|
|
||||||
button.btn.btn-danger#deleteUsers Delete Selected
|
|
||||||
div
|
|
||||||
div
|
|
||||||
form.well.form-inline#addUserToGroup
|
|
||||||
div
|
|
||||||
input(name="_csrf", type="hidden", value=csrfToken)
|
|
||||||
input(name="email", type="email", placeholder="someone@email.com")#newEmail.email.input-large
|
|
||||||
button.btn.btn-primary.addUser Add
|
|
||||||
div
|
|
||||||
div Add multiple emails seperated with commas or space.
|
|
||||||
|
|
||||||
|
|
||||||
- locals.supressDefaultJs = true
|
|
||||||
script(data-main='/js/SubscriptionGroupsManager.js', src='/js/libs/require.js')
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
67
services/web/public/coffee/app/directives/selectAll.coffee
Normal file
67
services/web/public/coffee/app/directives/selectAll.coffee
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
define [
|
||||||
|
"base"
|
||||||
|
], (App) ->
|
||||||
|
App.directive "selectAllList", () ->
|
||||||
|
return {
|
||||||
|
controller: ["$scope", ($scope) ->
|
||||||
|
# Selecting or deselecting all should apply to all projects
|
||||||
|
selectAll = () ->
|
||||||
|
$scope.$broadcast "select-all:select"
|
||||||
|
|
||||||
|
deselectAll = () ->
|
||||||
|
$scope.$broadcast "select-all:deselect"
|
||||||
|
|
||||||
|
clearSelectAllState = () ->
|
||||||
|
$scope.$broadcast "select-all:clear"
|
||||||
|
|
||||||
|
return {
|
||||||
|
clearSelectAllState: clearSelectAllState
|
||||||
|
selectAll: selectAll
|
||||||
|
deselectAll: deselectAll
|
||||||
|
}
|
||||||
|
]
|
||||||
|
link: (scope, element, attrs) ->
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
App.directive "selectAll", () ->
|
||||||
|
return {
|
||||||
|
require: "^selectAllList"
|
||||||
|
link: (scope, element, attrs, selectAllListController) ->
|
||||||
|
scope.$on "select-all:clear", () ->
|
||||||
|
element.prop("checked", false)
|
||||||
|
|
||||||
|
element.change () ->
|
||||||
|
if element.is(":checked")
|
||||||
|
selectAllListController.selectAll()
|
||||||
|
else
|
||||||
|
selectAllListController.deselectAll()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
App.directive "selectIndividual", () ->
|
||||||
|
return {
|
||||||
|
require: "^selectAllList"
|
||||||
|
scope: {
|
||||||
|
ngModel: "="
|
||||||
|
}
|
||||||
|
link: (scope, element, attrs, selectAllListController) ->
|
||||||
|
ignoreChanges = false
|
||||||
|
|
||||||
|
scope.$watch "ngModel", (value) ->
|
||||||
|
if value? and !ignoreChanges
|
||||||
|
selectAllListController.clearSelectAllState()
|
||||||
|
|
||||||
|
scope.$on "select-all:select", () ->
|
||||||
|
ignoreChanges = true
|
||||||
|
scope.$apply () ->
|
||||||
|
scope.ngModel = true
|
||||||
|
ignoreChanges = false
|
||||||
|
|
||||||
|
scope.$on "select-all:deselect", () ->
|
||||||
|
ignoreChanges = true
|
||||||
|
scope.$apply () ->
|
||||||
|
scope.ngModel = false
|
||||||
|
ignoreChanges = false
|
||||||
|
}
|
|
@ -3,12 +3,14 @@ define [
|
||||||
"main/user-details"
|
"main/user-details"
|
||||||
"main/account-settings"
|
"main/account-settings"
|
||||||
"main/plans"
|
"main/plans"
|
||||||
|
"main/group-members"
|
||||||
"directives/asyncForm"
|
"directives/asyncForm"
|
||||||
"directives/stopPropagation"
|
"directives/stopPropagation"
|
||||||
"directives/focus"
|
"directives/focus"
|
||||||
"directives/equals"
|
"directives/equals"
|
||||||
"directives/fineUpload"
|
"directives/fineUpload"
|
||||||
"directives/onEnter"
|
"directives/onEnter"
|
||||||
|
"directives/selectAll"
|
||||||
"filters/formatDate"
|
"filters/formatDate"
|
||||||
], () ->
|
], () ->
|
||||||
angular.bootstrap(document.body, ["SharelatexApp"])
|
angular.bootstrap(document.body, ["SharelatexApp"])
|
54
services/web/public/coffee/app/main/group-members.coffee
Normal file
54
services/web/public/coffee/app/main/group-members.coffee
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
define [
|
||||||
|
"base"
|
||||||
|
], (App) ->
|
||||||
|
App.controller "GroupMembersController", ($scope, queuedHttp) ->
|
||||||
|
$scope.users = window.users
|
||||||
|
$scope.groupSize = window.groupSize
|
||||||
|
$scope.selectedUsers = []
|
||||||
|
|
||||||
|
$scope.inputs =
|
||||||
|
emails: ""
|
||||||
|
|
||||||
|
parseEmails = (emailsString)->
|
||||||
|
regexBySpaceOrComma = /[\s,]+/
|
||||||
|
emails = emailsString.split(regexBySpaceOrComma)
|
||||||
|
emails = _.map emails, (email)->
|
||||||
|
email = email.trim()
|
||||||
|
emails = _.select emails, (email)->
|
||||||
|
email.indexOf("@") != -1
|
||||||
|
return emails
|
||||||
|
|
||||||
|
$scope.addMembers = () ->
|
||||||
|
emails = parseEmails($scope.inputs.emails)
|
||||||
|
for email in emails
|
||||||
|
queuedHttp
|
||||||
|
.post("/subscription/group/user", {
|
||||||
|
email: email,
|
||||||
|
_csrf: window.csrfToken
|
||||||
|
})
|
||||||
|
.success (data) ->
|
||||||
|
$scope.users.push data.user if data.user?
|
||||||
|
$scope.inputs.emails = ""
|
||||||
|
|
||||||
|
$scope.removeMembers = () ->
|
||||||
|
for user in $scope.selectedUsers
|
||||||
|
do (user) ->
|
||||||
|
queuedHttp({
|
||||||
|
method: "DELETE",
|
||||||
|
url: "/subscription/group/user/#{user._id}"
|
||||||
|
headers:
|
||||||
|
"X-Csrf-Token": window.csrfToken
|
||||||
|
})
|
||||||
|
.success () ->
|
||||||
|
index = $scope.users.indexOf(user)
|
||||||
|
return if index == -1
|
||||||
|
$scope.users.splice(index, 1)
|
||||||
|
$scope.selectedUsers = []
|
||||||
|
|
||||||
|
$scope.updateSelectedUsers = () ->
|
||||||
|
$scope.selectedUsers = $scope.users.filter (user) -> user.selected
|
||||||
|
|
||||||
|
App.controller "GroupMemberListItemController", ($scope) ->
|
||||||
|
$scope.$watch "user.selected", (value) ->
|
||||||
|
if value?
|
||||||
|
$scope.updateSelectedUsers()
|
|
@ -1,71 +1,6 @@
|
||||||
define [
|
define [
|
||||||
"base"
|
"base"
|
||||||
], (App) ->
|
], (App) ->
|
||||||
App.directive "selectAllList", () ->
|
|
||||||
return {
|
|
||||||
controller: ["$scope", ($scope) ->
|
|
||||||
# Selecting or deselecting all should apply to all projects
|
|
||||||
selectAll = () ->
|
|
||||||
$scope.$broadcast "select-all:select"
|
|
||||||
|
|
||||||
deselectAll = () ->
|
|
||||||
$scope.$broadcast "select-all:deselect"
|
|
||||||
|
|
||||||
clearSelectAllState = () ->
|
|
||||||
$scope.$broadcast "select-all:clear"
|
|
||||||
|
|
||||||
return {
|
|
||||||
clearSelectAllState: clearSelectAllState
|
|
||||||
selectAll: selectAll
|
|
||||||
deselectAll: deselectAll
|
|
||||||
}
|
|
||||||
]
|
|
||||||
link: (scope, element, attrs) ->
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
App.directive "selectAll", () ->
|
|
||||||
return {
|
|
||||||
require: "^selectAllList"
|
|
||||||
link: (scope, element, attrs, selectAllListController) ->
|
|
||||||
scope.$on "select-all:clear", () ->
|
|
||||||
element.prop("checked", false)
|
|
||||||
|
|
||||||
element.change () ->
|
|
||||||
if element.is(":checked")
|
|
||||||
selectAllListController.selectAll()
|
|
||||||
else
|
|
||||||
selectAllListController.deselectAll()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
App.directive "selectIndividual", () ->
|
|
||||||
return {
|
|
||||||
require: "^selectAllList"
|
|
||||||
scope: {
|
|
||||||
ngModel: "="
|
|
||||||
}
|
|
||||||
link: (scope, element, attrs, selectAllListController) ->
|
|
||||||
ignoreChanges = false
|
|
||||||
|
|
||||||
scope.$watch "ngModel", (value) ->
|
|
||||||
if value? and !ignoreChanges
|
|
||||||
selectAllListController.clearSelectAllState()
|
|
||||||
|
|
||||||
scope.$on "select-all:select", () ->
|
|
||||||
ignoreChanges = true
|
|
||||||
scope.$apply () ->
|
|
||||||
scope.ngModel = true
|
|
||||||
ignoreChanges = false
|
|
||||||
|
|
||||||
scope.$on "select-all:deselect", () ->
|
|
||||||
ignoreChanges = true
|
|
||||||
scope.$apply () ->
|
|
||||||
scope.ngModel = false
|
|
||||||
ignoreChanges = false
|
|
||||||
}
|
|
||||||
|
|
||||||
App.factory "queuedHttp", ["$http", "$q", ($http, $q) ->
|
App.factory "queuedHttp", ["$http", "$q", ($http, $q) ->
|
||||||
pendingRequests = []
|
pendingRequests = []
|
||||||
inflight = false
|
inflight = false
|
||||||
|
|
|
@ -68,16 +68,16 @@ form.project-search {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ul.project-list {
|
ul.structured-list {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
overflow-y: scroll;
|
overflow-y: auto;
|
||||||
li {
|
li {
|
||||||
border-bottom: 1px solid @gray-lightest;
|
border-bottom: 1px solid @gray-lightest;
|
||||||
padding: (@line-height-computed / 4) 0;
|
padding: (@line-height-computed / 4) 0;
|
||||||
&:first-child {
|
&:first-child {
|
||||||
.last-modified, .owner {
|
.header {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,6 +101,11 @@ ul.project-list {
|
||||||
.select-item, .select-all {
|
.select-item, .select-all {
|
||||||
margin-left: @line-height-computed / 4;
|
margin-left: @line-height-computed / 4;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.project-list {
|
||||||
|
li {
|
||||||
.last-modified, .owner {
|
.last-modified, .owner {
|
||||||
font-size: .8rem;
|
font-size: .8rem;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue