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

Optionally enable/disable features on contact types #227

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ To use the User Management Tool with your CHT project, you'll need to create a n
`contact_types.contact_properties` | Array<ConfigProperty> | Defines the attributes which are collected and set on the user's primary contact doc. See [ConfigProperty](#ConfigProperty).
`contact_types.deactivate_users_on_replace` | boolean | Controls what should happen to the defunct contact and user documents when a user is replaced. When `false`, the contact and user account will be deleted. When `true`, the contact will be unaltered and the user account will be assigned the role `deactivated`. This allows for account restoration.
`contact_types.hint` | string | Provide a brief hint or description to clarify the expected input for the property.
`contact_types.can_create` | boolean | Optionally disable/enable creating places of this type. Defaults to true.
`contact_types.can_replace_contact` | boolean | Optionally disable/enable replacing contacts for places of this type. Defaults to true.
`contact_types.can_move` | boolean | Optionally disable/enable moving places of this type. Defaults to true.
`logoBase64` | Image in base64 | Logo image for your project

#### ConfigProperty
Expand Down
37 changes: 36 additions & 1 deletion src/config/chis-ke/config.json

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions src/config/config-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const CONFIG_MAP: { [key: string]: PartnerConfig } = {
'CHIS-KE': kenyaConfig,
'CHIS-UG': ugandaConfig,
'CHIS-TG': togoConfig,
'CHIS-CIV': civConfig
'CHIS-CIV': civConfig,
};

export default function getConfigByKey(key: string = 'CHIS-KE'): PartnerConfig {
Expand All @@ -17,7 +17,9 @@ export default function getConfigByKey(key: string = 'CHIS-KE'): PartnerConfig {
const result = CONFIG_MAP[usingKey];
if (!result) {
const available = JSON.stringify(Object.keys(CONFIG_MAP));
throw Error(`Failed to start: Cannot find configuration "${usingKey}". Configurations available are ${available}`);
throw Error(
`Failed to start: Cannot find configuration '${usingKey}'. Configurations available are ${available}`
);
}

return result;
Expand Down
8 changes: 7 additions & 1 deletion src/config/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import _ from 'lodash';
import { ChtApi, PlacePayload } from '../lib/cht-api';
import getConfigByKey from './config-factory';
import getConfigByKey from './config-factory';

export type ConfigSystem = {
domains: AuthenticationInfo[];
Expand All @@ -26,6 +26,9 @@ export type ContactType = {
contact_properties: ContactProperty[];
deactivate_users_on_replace: boolean;
hint?: string;
can_create?: boolean;
can_replace_contact?: boolean;
can_move?: boolean;
};

export type HierarchyConstraint = {
Expand Down Expand Up @@ -122,6 +125,9 @@ export class Config {
}

public static hasMultipleRoles(contactType: ContactType): boolean {
if (contactType.can_move && (contactType.can_create === false && contactType.can_replace_contact === false)) {
return false;
}
if (!contactType.user_role.length || contactType.user_role.some(role => !role.trim())) {
throw Error(`unvalidatable config: 'user_role' property is empty or contains empty strings`);
}
Expand Down
24 changes: 17 additions & 7 deletions src/liquid/app/nav.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,20 @@
</a>
<div class="navbar-dropdown">
{% for placeType in contactTypes %}
<div class="navbar-item">
<b>{{placeType.friendly}}</b>
</div>
<a class="navbar-item" href="/add-place?type={{ placeType.name }}&op=new">Create New</a>
<a class="navbar-item" href="/add-place?type={{ placeType.name }}&op=replace">Replace Existing</a>
<a class="navbar-item" href="/add-place?type={{ placeType.name }}&op=bulk">Upload from CSV</a>
{% if placeType.can_create or placeType.can_create == undefined or placeType.can_replace_contact or placeType.can_replace_contact == undefined %}
<div class="navbar-item">
<b>{{placeType.friendly}}</b>
</div>
{%endif%}
{% if placeType.can_create == undefined or placeType.can_create %}
<a class="navbar-item" href="/add-place?type={{ placeType.name }}&op=new">Create New</a>
{%endif%}
{% if placeType.can_replace_contact == undefined or placeType.can_replace_contact %}
<a class="navbar-item" href="/add-place?type={{ placeType.name }}&op=replace">Replace Existing</a>
{%endif%}
{% if placeType.can_create == undefined or placeType.can_create %}
<a class="navbar-item" href="/add-place?type={{ placeType.name }}&op=bulk">Upload from CSV</a>
{%endif%}
{% endfor%}
</div>
</div>
Expand All @@ -35,7 +43,9 @@
</a>
<div class="navbar-dropdown">
{% for placeType in contactTypes %}
<a class="navbar-item" href="/move/{{ placeType.name }}">Move {{placeType.friendly}}</a>
{% if placeType.can_move == undefined or placeType.can_move %}
<a class="navbar-item" href="/move/{{ placeType.name }}">Move {{placeType.friendly}}</a>
{%endif%}
{% endfor%}
</div>
</div>
Expand Down
16 changes: 12 additions & 4 deletions src/liquid/place/directive_1_get_started.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,18 @@
<span class="material-symbols-outlined">add</span> Add {{contactType.friendly}}
</a>
<div class="navbar-dropdown">
<a class="navbar-item" href="/add-place?type={{ contactType.name }}&op=new">Create New</a>
<a class="navbar-item" href="/add-place?type={{ contactType.name }}&op=replace">Replace Existing</a>
<a class="navbar-item" href="/add-place?type={{ contactType.name }}&op=bulk">Upload from CSV</a>
<a class="navbar-item" href="/move/{{ contactType.name }}">Move</a>
{% if contactType.can_create == undefined or contactType.can_create %}
<a class="navbar-item" href="/add-place?type={{ contactType.name }}&op=new">Create New</a>
{%endif%}
{% if contactType.can_replace_contact == undefined or contactType.can_replace_contact %}
<a class="navbar-item" href="/add-place?type={{ contactType.name }}&op=replace">Replace Existing</a>
{%endif%}
{% if contactType.can_create == undefined or contactType.can_create %}
<a class="navbar-item" href="/add-place?type={{ contactType.name }}&op=bulk">Upload from CSV</a>
{%endif%}
{% if contactType.can_move == undefined or contactType.can_move %}
<a class="navbar-item" href="/move/{{ contactType.name }}">Move</a>
{%endif%}
</div>
</div>
</div>
Expand Down
40 changes: 21 additions & 19 deletions src/liquid/place/list.html
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
<div id="place_list">
{% for contactType in contactTypes %}
<div id="{{contactType.name}}" class="mb-6">
<h2 class="title is-4">{{contactType.friendly}}</h2>
{% if contactType.places.length > 0 %}
<table
id="table_places"
class="table is-fullwidth is-striped is-hoverable"
>
{% include "components/table_header.html" contactType=contactType %}
<tbody>
{% for place in contactType.places %}
{% include "components/place_item.html" %}
{% endfor%}
</tbody>
</table>
{% else %}
<div class="notification is-white">
<i>No Results</i>
</div>
{% endif %}
{% if contactType.can_create or contactType.can_create == undefined or contactType.can_replace_contact or contactType.can_replace_contact == undefined %}
<div id="{{contactType.name}}" class="mb-6">
<h2 class="title is-4">{{contactType.friendly}}</h2>
{% if contactType.places.length > 0 %}
<table
id="table_places"
class="table is-fullwidth is-striped is-hoverable"
>
{% include "components/table_header.html" contactType=contactType %}
<tbody>
{% for place in contactType.places %}
{% include "components/place_item.html" %}
{% endfor%}
</tbody>
</table>
{% else %}
<div class="notification is-white">
<i>No Results</i>
</div>
{% endif %}
{%endif%}
{% endfor %}
</div>
22 changes: 12 additions & 10 deletions src/liquid/place/list_lazy.html
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
<div id="place_list" hx-trigger="load" hx-get="/app/list" hx-target="this" hx-swap="outerHTML">
{% for contactType in contactTypes %}
<div id="{{contactType.name}}" class="mb-6">
<h2 class="title is-4">{{contactType.friendly}}</h2>
<div>
<table id="table_places" class="table is-fullwidth is-striped is-hoverable">
{% include "components/table_header.html" contactType=contactType %}
</table>
<div class="container p-6 is-flex is-justify-content-center is-align-content-center">
<img src="/public/spinner.gif" alt="Loading data" />
</div>
</div>
{% if contactType.can_create or contactType.can_create == undefined or contactType.can_replace_contact or contactType.can_replace_contact == undefined %}
<div id="{{contactType.name}}" class="mb-6">
<h2 class="title is-4">{{contactType.friendly}}</h2>
<div>
<table id="table_places" class="table is-fullwidth is-striped is-hoverable">
{% include "components/table_header.html" contactType=contactType %}
</table>
<div class="container p-6 is-flex is-justify-content-center is-align-content-center">
<img src="/public/spinner.gif" alt="Loading data" />
</div>
</div>
{%endif%}
{% endfor %}
</div>
</div>
5 changes: 5 additions & 0 deletions src/routes/add-place.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ export default async function addPlace(fastify: FastifyInstance) {
? Config.getContactType(queryParams.type)
: contactTypes[contactTypes.length - 1];
const op = queryParams.op || 'new';
if ((op === 'new' && contactType.can_create === false) ||
(op === 'replace' && contactType.can_replace_contact === false)) {
resp.code(404).type('text/html').send('Not Found');
return;
}
const tmplData = {
view: 'add',
logo: Config.getLogoBase64(),
Expand Down
4 changes: 4 additions & 0 deletions src/routes/move.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ export default async function sessionCache(fastify: FastifyInstance) {
const contactTypes = Config.contactTypes();

const contactType = Config.getContactType(placeType);
if (contactType.can_move === false) {
resp.code(404).type('text/html').send('Not Found');
return;
}
const tmplData = {
view: 'move',
op: 'move',
Expand Down