Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WP-543: Setup APCD API Client and integrate users endpoints #323

Open
wants to merge 28 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
7c63ff2
Initial commit - using react with APCD admin registration list page (…
chandra-tacc Feb 27, 2024
139cc78
Initial commit - using react with APCD admin registration list page (…
chandra-tacc Feb 27, 2024
c4936c2
Update README.md for APCD
chandra-tacc Feb 27, 2024
d32a189
Fix dockerfile for static
chandra-tacc Mar 20, 2024
df65e25
Initial commit - using react with APCD admin registration list page (…
chandra-tacc Feb 27, 2024
28c4e38
Update README.md for APCD
chandra-tacc Feb 27, 2024
87ecbc2
Fix dockerfile for static
chandra-tacc Mar 20, 2024
09290c0
Merge branch 'feat/apcd-react-conversion' of github.com:TACC/Core-CMS…
chandra-tacc Mar 20, 2024
e6d6616
Install nodejs as part of docker image
chandra-tacc Mar 20, 2024
e75e6f5
Docker compose dev adjustments for react
chandra-tacc Jun 10, 2024
f6ecd5f
Merge branch 'main' into feat/apcd-react-conversion
chandra-tacc Jun 10, 2024
7827d80
Merge branch 'feat/apcd-react-conversion' of github.com:TACC/Core-CMS…
edmondsgarrett Jun 10, 2024
39fd3a1
task/WP-443 Convert Admin Submissions to React (#288)
shayanaijaz Jun 14, 2024
3455b5d
Task/wp 425 admin user listing (#286)
van-go Jun 14, 2024
f9036d0
Fix merge issue
chandra-tacc Jun 14, 2024
86d0fab
[ACPD React Conversion]: Setup vite local and add base template with …
chandra-tacc Jul 25, 2024
c132a2a
Update README.md to reflect new changes
chandra-tacc Jul 25, 2024
10f73ac
Initial version (#299)
jmartinez-tacc Aug 5, 2024
c07113e
Task/WP-429 list registrations: filters, pagination and view modal (#…
chandra-tacc Aug 5, 2024
599926c
List Admin Extensions React Conversion (#289)
happycodemonkey Aug 16, 2024
451130f
Add formik, reactstrap, yup for APCD React forms (#302)
sophia-massie Aug 16, 2024
38555a2
Task/wp 425 view edit modals (#297)
van-go Sep 5, 2024
92074ee
Submitter Registration List using React (#307)
chandra-tacc Sep 6, 2024
1563a65
add apcd user client; refresh/create on login
rstijerina Feb 2, 2024
ea1ebb0
fix import
rstijerina Feb 2, 2024
d652d29
fix response message
rstijerina Feb 2, 2024
1607708
fix import; fix exception type
rstijerina Feb 2, 2024
ab03a2f
APCD API Client and integrating with react UI
chandra-tacc Jun 9, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Core-CMS-Custom
Submodule Core-CMS-Custom added at 9fb943
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
{{ block.super }}

{% include "./assets_custom.html" %}
{% endblock assets_custom %}
{% endblock assets_custom %}
9 changes: 9 additions & 0 deletions apcd-cms/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,14 @@ FROM taccwma/core-cms:5473db7
WORKDIR /code

COPY /src/apps /code/apps
COPY /src/client /code/client
COPY /src/taccsite_custom /code/taccsite_custom
COPY /src/taccsite_cms /code/taccsite_cms

# install node 20.x
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
RUN apt-get install -y nodejs

RUN chmod u+x /code/client/build_client.sh && /code/client/build_client.sh
RUN cp -R /code/client/dist/static/assets/. /code/taccsite_custom/apcd_cms/static/assets/
RUN cp -R /code/client/dist/react-assets.html /code/taccsite_custom/apcd_cms/templates/react-asset.html
9 changes: 5 additions & 4 deletions apcd-cms/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ DOCKERHUB_REPO := $(shell cat ./docker_repo.var)
DOCKER_TAG ?= $(shell git rev-parse --short HEAD)
DOCKER_IMAGE := $(DOCKERHUB_REPO):$(DOCKER_TAG)
DOCKER_IMAGE_LATEST := $(DOCKERHUB_REPO):latest
DOCKER_COMPOSE_CMD := $(shell if command -v docker-compose > /dev/null; then echo "docker-compose"; else echo "docker compose"; fi)

# `DOCKER_IMAGE_BRANCH` tag is the git tag for the commit if it exists, else the branch on which the commit exists
DOCKER_IMAGE_BRANCH := $(DOCKERHUB_REPO):$(shell git describe --exact-match --tags 2> /dev/null || git symbolic-ref --short HEAD)

#.PHONY: build
build:
docker-compose -f docker-compose.dev.yml build
$(DOCKER_COMPOSE_CMD) -f docker-compose.dev.yml build

.PHONY: build-full
build-full:
Expand All @@ -27,12 +28,12 @@ publish-latest:

.PHONY: start
start:
docker-compose -f docker-compose.dev.yml up
$(DOCKER_COMPOSE_CMD) -f docker-compose.dev.yml up

.PHONY: stop
stop:
docker-compose -f docker-compose.dev.yml down
$(DOCKER_COMPOSE_CMD) -f docker-compose.dev.yml down

.PHONY: stop-full
stop-v:
docker-compose -f docker-compose.dev.yml down -v
$(DOCKER_COMPOSE_CMD) -f docker-compose.dev.yml down -v
53 changes: 53 additions & 0 deletions apcd-cms/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,56 @@ An extension of the [Core CMS](https://github.com/TACC/Core-CMS) project
## Basics

See [Core-CMS-Custom](../README.md).


## Build

1. cd to acpd-cms
2. run make build
3. run make start
4. cd to apcd-cms/src/client
5. run npm ci
6. run npm run build
7. run npm run dev
8. Make code changes and observe changes live in browser.


## Converting existing page to react based page

### Backend

1. Update urls.py
+ Make the default page return as TemplateView.as_view(template_name='<template_name')
+ Add api end points. These endpoints are used in client.

2. Update views.py

+ import JsonResponse
+ remove Template building
+ Adjust context to return Json.
+ send JsonResponse



### Client

1. Define hook
+ Add method to retrieve data from server
+ Add types in index.ts

2. Defining component
- Add one or more component as tsx file.
- Export the component
- Add exports in index.ts

3. Update apcd-cms/src/client/src/main.tsx:
- add import for your component
- update componentMap to add the


### Template

* Update the first line: standard.html to use this {% extends "apcd_cms/templates/standard.html" %}
* Add a div element where the component will render, Example: (make the id name distinct)
<div id="list-registrations-root"></div>

2 changes: 2 additions & 0 deletions apcd-cms/docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ services:
volumes:
- core_cms_postgres_data:/var/lib/postgresql/data
hostname: core_cms_postgres
ports:
- 127.0.0.1:5432:5432
container_name: core_cms_postgres
networks:
- core_cms_net
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
{% extends "standard.html" %}
{% extends "apcd_cms/templates/standard.html" %}
{% load static %}

{% block content %}
<link rel="stylesheet" href="{% static 'apcd-cms/css/table.css' %}">
<link rel="stylesheet" href="{% static 'apcd-cms/css/modal.css' %}">
<link rel="stylesheet" href="{% static 'admin_exception/css/table.css' %}">
<div class="container">
{% include "nav_cms_breadcrumbs.html" %}
Expand All @@ -11,104 +12,11 @@ <h1>View Exception Requests</h1>
<hr />
<p style="margin-bottom: 30px">All submitted exception requests</p>
<hr/>
<div class="filter-container">
<div class="filter-content">
<span><b>Filter by Status: </b></span>
<select id="statusFilter" class="status-filter" onchange="filterTableByStatus()">
{% for option in status_options %}
<option class="dropdown-text" {% if option == selected_status %}selected{% endif %}>{{ option }}</option>
{% endfor %}
</select>
<span><b>Filter by Organization: </b></span>
<select id="organizationFilter" class="status-filter org-filter" onchange="sortByOrg()">
<!-- Value set here so dropdown is not blank but set to All-->
{% for option in org_options %}
<option class="dropdown-text" {% if option == selected_org %}selected{% endif %}>{{ option }}</option>
{% endfor %}
</select>
{% if selected_status or selected_org %}
<button onclick="clearSelections()">Clear Options</button>
{% endif %}
</div>
</div>
<table id="exception-table" class="exception-table">
<thead>
<tr>
{% for k in header %}
<th>{{k}}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for r in page %}
<tr>
<!-- So when page is shrunk, rows match with the former columns -->
<td class="created">{{r.created_at}}</td>
<td class="entity_name">{{r.entity_name}}</td>
<td class="requestor_name">{{r.requestor_name}}</td>
<td class="exception_type">{{r.request_type}}</td>
<td class="outcome">{{r.outcome}}</td>
<td class="status">{{r.status}}</td>
<td class="modal-cell">
{% include "view_admin_exception_modal.html" %}
{% include "edit_exception_modal.html" %}
<select id='actionsDropdown_{{r.exception_id}}' onchange="openAction('{{r.exception_id}}')">
<div class="filter-container">
<div class="filter-content">
<option value="">Select Action</option>
<option value="viewAdminExceptions">View Record</option>
<option value="editException">Edit Record</option>
</div>
</div>
</select>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div id="admin-exceptions-root"></div>
{% include 'paginator.html' %}
</div>
<script>

function filterTableByStatus() {
var dropdown, statusFilter, xhr, url_params, url;
dropdown = document.getElementById("statusFilter");
statusFilter = dropdown.value;
url_params = `?status=${statusFilter}`;
{% if selected_org %}
url_params = `?status=${statusFilter}&org={{selected_org}}`;
{% endif %}
url = `/administration/list-exceptions/${url_params}`;
xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.send();
window.location.href = url;
window.location.load();
}

function sortByOrg() {
var orgDropdown, orgValue, url_params, url, xhr;
orgDropdown = document.getElementById('organizationFilter');
orgValue = orgDropdown.value;
url_params = `?org=${orgValue}`;
{% if selected_status %}
url_params = `?status={{selected_status}}&org=${orgValue}`;
{% endif %}
url = `/administration/list-exceptions/${url_params}`;
xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.send();
window.location.href = url;
window.location.load();
}
function clearSelections() {
var xhr;
xhr = new XMLHttpRequest();
xhr.open('GET', '/administration/list-exceptions/')
xhr.send()
window.location.href = '/administration/list-exceptions/';
window.location.load();
}
function openAction(exception_id) {
var actionsDropdown, selectedOption, modal_id;
actionsDropdown = document.getElementById(`actionsDropdown_${exception_id}`);
Expand Down
11 changes: 9 additions & 2 deletions apcd-cms/src/apps/admin_exception/urls.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
from django.urls import path
from django.views.generic import TemplateView
from apps.admin_exception.views import AdminExceptionsTable

app_name = 'admin_exception'
urlpatterns = [
path('list-exceptions/', AdminExceptionsTable.as_view(), name="list_exceptions"),
path('list-exceptions/', TemplateView.as_view(template_name='list_admin_exception.html'), name="list_exceptions"),
path('list-exceptions/<str:status>', AdminExceptionsTable.as_view(), name='status'),
path('list-exceptions/<str:org>', AdminExceptionsTable.as_view(), name='org'),
path('list-exceptions/<str:status><str:org>', AdminExceptionsTable.as_view(), name='status_org')
path('list-exceptions/<str:status><str:org>', AdminExceptionsTable.as_view(), name='status_org'),
path(r'list-exceptions/api/', AdminExceptionsTable.as_view(), name='admin_exceptions_table_api'),
path(r'list-exceptions/api/?status=(?P<status>)/', AdminExceptionsTable.as_view(),
name='admin_exceptions_table_api'),
path(r'list-exceptions/api/?org=(?P<org>)/', AdminExceptionsTable.as_view(), name='admin_exceptions_table_api'),
path(r'list-exceptions/api/?status=(?P<status>)&org=(?P<org>)/', AdminExceptionsTable.as_view(),
name='admin_exceptions_table_api')
]
47 changes: 28 additions & 19 deletions apcd-cms/src/apps/admin_exception/views.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from django.http import HttpResponseRedirect, HttpResponse
from django.http import HttpResponseRedirect, HttpResponse, JsonResponse
from django.core.paginator import Paginator, EmptyPage
from django.views.generic.base import TemplateView
from django.template import loader
Expand All @@ -7,6 +7,7 @@
from apps.utils.utils import title_case
from apps.utils.utils import table_filter
from apps.components.paginator.paginator import paginator
from datetime import date as datetimeDate
from dateutil import parser
import logging

Expand Down Expand Up @@ -47,22 +48,34 @@ def dispatch(self, request, *args, **kwargs):
return HttpResponseRedirect('/')
return super(AdminExceptionsTable, self).dispatch(request, *args, **kwargs)

def get_context_data(self, *args, **kwargs):
def get(self, *args, **kwargs):

context = super(AdminExceptionsTable, self).get_context_data(*args, **kwargs)
# context = super(AdminExceptionsTable, self).get_context_data(*args, **kwargs)

exception_content = get_all_exceptions()
# To get filters from params
queryStr = ''
status_filter = self.request.GET.get('status')
org_filter = self.request.GET.get('org')

context = self.get_exception_list_json(exception_content, *args, **kwargs)
return JsonResponse({'response': context})

def get_exception_list_json(self, exception_content, *args, **kwargs):
context = {}

context['header'] = ['Created', 'Entity Organization', 'Requestor Name', 'Exception Type', 'Outcome', 'Status', 'Actions']
context['status_options'] = ['All']
context['org_options'] = ['All']
context['outcome_options'] = []
context['exceptions'] = []
context['action_options'] = ['Select Action', 'View Record', 'Edit Record']

try:
page_num = int(self.request.GET.get('page'))
except:
page_num = 1

def _set_exceptions(exception):
queryStr = ''
status_filter = self.request.GET.get('status')
org_filter = self.request.GET.get('org')
def _set_exception(exception):
return {
'exception_id': exception[0],
'submitter_id': exception[1],
Expand All @@ -88,14 +101,6 @@ def _set_exceptions(exception):
'entity_name': exception[21],
'data_file_name': exception[22]
}

context['header'] = ['Created', 'Entity Organization', 'Requestor Name', 'Exception Type', 'Outcome', 'Status', 'Actions']
context['status_options'] = ['All']
context['org_options'] = ['All']
context['outcome_options'] = []
# In case you need to access an exception directly within the template in the future
context['exceptions'] = []

def getDate(row):
date = row[6]
return date if date is not None else parser.parse('1-1-0001')
Expand All @@ -106,9 +111,9 @@ def getDate(row):
exception_table_entries = []
for exception in exception_content:
# to be used by paginator
exception_table_entries.append(_set_exceptions(exception))
exception_table_entries.append(_set_exception(exception))
# to be able to access any exception in a template using exceptions var in the future
context['exceptions'].append(_set_exceptions(exception))
context['exceptions'].append(_set_exception(exception))
entity_name = title_case(exception[21])
status = title_case(exception[19])
outcome = title_case(exception[5])
Expand Down Expand Up @@ -140,7 +145,11 @@ def getDate(row):
exception_table_entries = table_filter(org_filter.replace("(", "").replace(")",""), exception_table_entries, 'entity_name')

context['query_str'] = queryStr
context.update(paginator(self.request, exception_table_entries))
# context.update(paginator(self.request, exception_table_entries))
page_info = paginator(self.request, exception_table_entries)
context['page'] = [{'entity_name': obj['entity_name'], 'created_at': obj['created_at'], 'request_type': obj['request_type'], 'requestor_name': obj['requestor_name'], 'outcome': obj['outcome'], 'status': obj['status'], 'exception_id': obj['exception_id']} for obj in page_info['page']]
context['pagination_url_namespaces'] = 'admin_exception:list_exceptions'

return context


Loading