Skip to content

Commit

Permalink
feat: Added UI for showing/removing admins of a project [PT-186428490]
Browse files Browse the repository at this point in the history
Adds list of admins in the project edit form and allows users to remove project admins.

Due to the concern of leaking emails only site admins can add project admins to projects.

This also updates some styling of the main admin ui.
  • Loading branch information
dougmartin committed Nov 20, 2023
1 parent 5102a04 commit 2573943
Show file tree
Hide file tree
Showing 8 changed files with 358 additions and 142 deletions.
127 changes: 88 additions & 39 deletions app/assets/javascripts/lara-typescript.js
Original file line number Diff line number Diff line change
Expand Up @@ -124633,17 +124633,21 @@ var ProjectSettingsForm = function (_a) {
project = _g[0],
setProject = _g[1];

var _h = (0, react_1.useState)(false),
projectLoaded = _h[0],
setProjectLoaded = _h[1];
var _h = (0, react_1.useState)([]),
admins = _h[0],
setAdmins = _h[1];

var _j = (0, react_1.useState)(false),
isNewProject = _j[0],
setIsNewProject = _j[1];
projectLoaded = _j[0],
setProjectLoaded = _j[1];

var _k = (0, react_1.useState)(false),
projectSaved = _k[0],
setProjectSaved = _k[1];
isNewProject = _k[0],
setIsNewProject = _k[1];

var _l = (0, react_1.useState)(false),
projectSaved = _l[0],
setProjectSaved = _l[1];

(0, react_1.useEffect)(function () {
if (id) {
Expand Down Expand Up @@ -124685,6 +124689,7 @@ var ProjectSettingsForm = function (_a) {
delete data.project.created_at;
delete data.project.updated_at;
setProject((0, convert_keys_1.snakeToCamelCaseKeys)(data.project));
setAdmins(data.admins);
setProjectLoaded(true);
setPageTitle("Edit " + data.project.title);
return [2
Expand Down Expand Up @@ -124741,11 +124746,9 @@ var ProjectSettingsForm = function (_a) {
var handleCollaboratorsImageUrlChange = handleTextInputChange("collaboratorsImageUrl");
var handleContactEmailChange = handleTextInputChange("contactEmail");
var handleCopyrightImageUrlChange = handleTextInputChange("copyrightImageUrl");
var handleFooterChange = handleTextareaChange("footer");
var handleFundersImageUrlChange = handleTextInputChange("fundersImageUrl");
var handleKeyChange = handleTextInputChange("projectKey");
var handleLogoApChange = handleTextInputChange("logoAp");
var handleLogoLaraChange = handleTextInputChange("logoLara");
var handleTitleChange = handleTextInputChange("title");
var handleUrlChange = handleTextInputChange("url");
var handleAboutChange = handleSlateRteChange("about", setAboutValue);
Expand Down Expand Up @@ -124804,6 +124807,55 @@ var ProjectSettingsForm = function (_a) {
});
};

var handleRemoveAdmin = function (admin) {
return function (e) {
var title = project.title.trim().length > 0 ? project.title : "this project";

if (confirm("Are you sure you want to remove " + admin.email + " as a Project Admin of " + title + "?")) {
setAdmins(function (prev) {
return prev.filter(function (a) {
return a.id !== admin.id;
});
});
}
};
};

var renderProjectAdmins = function () {
if (!id) {
return null;
}

var renderList = function () {
if (!projectLoaded) {
return React.createElement("div", {
className: "emphasis"
}, "Loading the admin list ...");
}

if (admins.length === 0) {
return React.createElement("div", {
className: "emphasis"
}, "There are no project admins assigned to this project. Please contact a site admin to add project admins to this project.");
}

return React.createElement("div", null, React.createElement("table", null, React.createElement("tbody", null, admins.map(function (admin) {
return React.createElement("tr", {
key: admin.id
}, React.createElement("td", null, admin.email), React.createElement("td", null, React.createElement("button", {
title: "Remove this project admin from the project",
onClick: handleRemoveAdmin(admin)
}, "DELETE")));
}))), React.createElement("div", {
className: "emphasis"
}, "Please contact a site admin to add additional project admins to this project."));
};

return React.createElement("div", {
className: "projectAdmins"
}, React.createElement("label", null, "Project Admins"), renderList());
};

if (isNewProject && projectSaved) {
return null;
}
Expand All @@ -124816,11 +124868,15 @@ var ProjectSettingsForm = function (_a) {
href: "/"
}, "Home"), " "), React.createElement("li", null, "\u00A0/ ", React.createElement("a", {
href: "/projects"
}, "Projects"), " "), React.createElement("li", null, "\u00A0/ ", pageTitle)))), React.createElement("h1", {
className: "title"
}, pageTitle), alertMessage && React.createElement("div", {
}, "Projects"), " "), React.createElement("li", null, "\u00A0/ ", pageTitle)))), React.createElement("div", {
className: "titleContainer"
}, React.createElement("h1", null, pageTitle), React.createElement("button", {
onClick: handleSaveProject
}, "Save")), alertMessage && React.createElement("div", {
className: "alertMessage"
}, alertMessage), React.createElement("dl", null, React.createElement("dt", null, React.createElement("label", {
}, alertMessage), React.createElement("div", {
className: "splitForm"
}, React.createElement("dl", null, React.createElement("dt", null, React.createElement("label", {
htmlFor: "project-title"
}, "Title")), React.createElement("dd", null, React.createElement("input", {
id: "project-title",
Expand All @@ -124840,19 +124896,8 @@ var ProjectSettingsForm = function (_a) {
})), React.createElement("dd", {
className: "inputNote"
}, "The project key is used across sites to synchronise project information. It must be a unique value."), React.createElement("dt", null, React.createElement("label", {
htmlFor: "project-logo-lara"
}, "LARA Runtime Logo URL")), React.createElement("dd", {
className: "hasNote"
}, React.createElement("input", {
id: "project-logo-lara",
name: "project[logo_lara]",
defaultValue: project.logoLara,
onChange: handleLogoLaraChange
})), React.createElement("dd", {
className: "inputNote"
}, "Image should be 228 pixels wide by 70 pixels high. If left blank, the Concord Consortium logo will be used by default."), React.createElement("dt", null, React.createElement("label", {
htmlFor: "project-logo-ap"
}, "Activity Player Logo URL")), React.createElement("dd", {
}, "Activity Header Logo URL")), React.createElement("dd", {
className: "hasNote"
}, React.createElement("input", {
id: "project-logo-ap",
Expand All @@ -124872,18 +124917,7 @@ var ProjectSettingsForm = function (_a) {
onChange: handleUrlChange
})), React.createElement("dd", {
className: "inputNote"
}, "When logo image is clicked, this is URL will launch in a new browser tab."), React.createElement("dt", null, React.createElement("label", {
htmlFor: "project-url"
}, "LARA Runtime Footer")), React.createElement("dd", {
className: "hasNote"
}, React.createElement("textarea", {
id: "project-footer",
name: "project[footer]",
defaultValue: project.footer,
onChange: handleFooterChange
})), React.createElement("dd", {
className: "inputNote"
}, "Raw HTML can be entered into this legacy field.")), React.createElement("h2", null, "Activity Player Footer"), React.createElement("dl", null, React.createElement("dt", null, React.createElement("label", null, "Copyright/Attribution Text")), React.createElement("dd", null, React.createElement("div", {
}, "When logo image is clicked, this is URL will launch in a new browser tab.")), renderProjectAdmins()), React.createElement("h2", null, "Activity Homepage Footer"), React.createElement("dl", null, React.createElement("dt", null, React.createElement("label", null, "Copyright/Attribution Text")), React.createElement("dd", null, React.createElement("div", {
className: "slateContainer"
}, React.createElement(slate_editor_1.SlateContainer, {
value: copyrightValue,
Expand Down Expand Up @@ -124943,8 +124977,23 @@ var ProjectSettingsForm = function (_a) {
onChange: handleContactEmailChange
})), React.createElement("dd", {
className: "inputNote"
}, "Provide a valid email address for users to contact your project team. When this is provided, your contact email will be displayed in the footer.")), React.createElement("button", {
id: "save-button",
}, "Provide a valid email address for users to contact your project team. When this is provided, your contact email will be displayed in the footer.")), React.createElement("h2", null, "Legacy LARA Fields"), React.createElement("dl", null, React.createElement("dd", {
className: "inputNote"
}, "These fields are no longer used but are shown here in case you need to see or copy their values."), React.createElement("dt", null, React.createElement("label", {
htmlFor: "project-logo-lara"
}, "LARA Runtime Logo URL")), React.createElement("dd", null, React.createElement("input", {
id: "project-logo-lara",
name: "project[logo_lara]",
defaultValue: project.logoLara,
disabled: true
})), React.createElement("dt", null, React.createElement("label", {
htmlFor: "project-url"
}, "LARA Runtime Footer")), React.createElement("dd", null, React.createElement("textarea", {
id: "project-footer",
name: "project[footer]",
defaultValue: project.footer,
disabled: true
}))), React.createElement("button", {
onClick: handleSaveProject
}, "Save"));
};
Expand Down
52 changes: 48 additions & 4 deletions app/assets/stylesheets/lara-typescript.css
Original file line number Diff line number Diff line change
Expand Up @@ -4097,13 +4097,57 @@ button.bigButton svg {
.projectSettingsForm {
padding-bottom: 20px;
}
.projectSettingsForm .titleContainer {
padding: 1% 0;
display: flex;
justify-content: space-between;
align-items: center;
}
.projectSettingsForm .titleContainer h1, .projectSettingsForm .titleContainer button {
margin: 0;
}
.projectSettingsForm h2 {
color: var(--teal);
font-family: var(--font-family-bold);
font-size: 20px;
line-height: 1.2;
margin: 30px 0 20px;
margin: 20px 0;
padding: 0;
}
.projectSettingsForm .splitForm {
display: flex;
gap: 20px;
}
.projectSettingsForm .splitForm dl {
flex-grow: 1;
width: 100%;
}
.projectSettingsForm .splitForm .projectAdmins {
flex-grow: 1;
width: 25%;
}
.projectSettingsForm .splitForm .projectAdmins label {
font-size: 16px;
font-weight: 900;
}
.projectSettingsForm .splitForm .projectAdmins table {
margin: 10px 0;
width: 100%;
border-spacing: 10px;
}
.projectSettingsForm .splitForm .projectAdmins table button {
background: transparent;
border: none;
color: var(--dark-gray);
display: inline-block;
font-size: 12px;
margin: 0;
padding: 0;
text-transform: uppercase;
}
.projectSettingsForm .splitForm .projectAdmins .emphasis {
margin: 10px 0;
font-style: italic;
}
.projectSettingsForm .slateContainer {
border: solid 1.5px var(--med-gray);
Expand Down Expand Up @@ -4142,16 +4186,16 @@ button.bigButton svg {
border-radius: 4px;
font-family: var(--font-family-default);
font-size: 16px;
padding: 5px 10px 7px;
width: calc(50% - 23px);
padding: 5px 10px;
width: calc(100% - 23px);
-webkit-appearance: none;
}
.projectSettingsForm textarea {
height: 200px;
width: calc(100% - 23px);
}
.projectSettingsForm dl {
margin: 0 0 20px;
margin: 0;
padding: 0;
}
.projectSettingsForm dl dt {
Expand Down
5 changes: 5 additions & 0 deletions app/assets/stylesheets/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1227,6 +1227,11 @@ body.admin.edit, body.admin.new {
padding: 7px;
}
}
input[type=checkbox] {
vertical-align: middle;
position: relative;
bottom: 1px;
}
div.actions {
padding: 7px;
input {
Expand Down
5 changes: 3 additions & 2 deletions app/controllers/api/v1/projects_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ def index
# GET /api/v1/projects/1.json
def show
begin
@project = Project.find(params[:id])
@project = Project.includes(:admins).find(params[:id])
authorize! :manage, @project
render json: {project: @project}, status: 200
@admins = @project.admins.map { |a| {id: a.id, email: a.email} }
render json: {project: @project, admins: @admins}, status: 200
rescue ActiveRecord::RecordNotFound
render json: {error: "Project not found"}, status: 404
end
Expand Down
2 changes: 1 addition & 1 deletion app/views/admin/users/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<div>
<%= f.check_box :is_admin %>
<label class="normal" for="user_is_admin">
Admin
Site Admin
</label>
</div>
<div>
Expand Down
59 changes: 55 additions & 4 deletions lara-typescript/src/projects/components/project-settings-form.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,66 @@
.projectSettingsForm {
padding-bottom: 20px;

.titleContainer {
padding: 1% 0;
display: flex;
justify-content: space-between;
align-items: center;

h1, button {
margin: 0;
}
}

h2 {
color: var(--teal);
font-family: var(--font-family-bold);
font-size: 20px;
line-height: 1.2;
margin: 30px 0 20px;
margin: 20px 0;
padding: 0;
}

.splitForm {
display: flex;
gap: 20px;

dl {
flex-grow: 1;
width: 100%;
}
.projectAdmins {
flex-grow: 1;
width: 25%;

label {
font-size: 16px;
font-weight: 900;
}
table {
margin: 10px 0;
width: 100%;
border-spacing: 10px;

button {
background: transparent;
border: none;
color: var(--dark-gray);
display: inline-block;
font-size: 12px;
margin: 0;
padding: 0;
text-transform: uppercase;
}
}
.emphasis {
margin: 10px 0;
font-style: italic;
}
}
}


.slateContainer {
border: solid 1.5px var(--med-gray);
border-radius: 4px;
Expand Down Expand Up @@ -46,8 +97,8 @@
border-radius: 4px;
font-family: var(--font-family-default);
font-size: 16px;
padding: 5px 10px 7px;
width: calc(50% - 23px); // 20px padding + 3px border = 23
padding: 5px 10px;
width: calc(100% - 23px); // 20px padding + 3px border = 23
-webkit-appearance: none;
}
textarea {
Expand All @@ -56,7 +107,7 @@
}

dl {
margin: 0 0 20px;
margin: 0;
padding: 0;

dt {
Expand Down
Loading

0 comments on commit 2573943

Please sign in to comment.