Skip to content

Commit

Permalink
redirect if config supports it
Browse files Browse the repository at this point in the history
  • Loading branch information
freddieptf committed Feb 17, 2025
1 parent 7a34bab commit 542d2c5
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 56 deletions.
1 change: 1 addition & 0 deletions src/config/chis-ke/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@
"user_role": ["community_health_assistant"],
"username_from_place": false,
"deactivate_users_on_replace": false,
"can_assign_multiple": true,
"hierarchy": [
{
"friendly_name": "Sub County",
Expand Down
1 change: 1 addition & 0 deletions src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export type ContactType = {
place_properties: ContactProperty[];
contact_properties: ContactProperty[];
deactivate_users_on_replace: boolean;
can_assign_multiple?: boolean;
hint?: string;
};

Expand Down
17 changes: 8 additions & 9 deletions src/liquid/new/create_form.liquid
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
<div id="create_form_parent" class="is-flex is-flex-direction-column is-align-items-center">
<div>
<h3 class="is-size-4">New {{ contactType.contact_friendly }}</h3>
</div>
<form
class="mt-4"
autocomplete="off"
hx-target="#create_form_parent"
hx-swap="outerHTML"
hx-post="/new?place_type={{contactType.name}}"
style="max-width: 48rem;"
>
<div>
<h3 class="is-size-4">New {{ contactType.contact_friendly }}</h3>
</div>
<input name="place_type" value="{{contactType.name}}" hidden>

<p>Contact details</p>
Expand Down Expand Up @@ -43,10 +42,13 @@
{% render 'new/new_place_btn.liquid' with contactType.name as place_type %}
</div>

<div class="field is-grouped is-grouped-right mt-4">
<div class="field mt-4 is-flex is-justify-content-space-between">
<button class="button is-small is-ghost px-0" hx-post="/new?place_type={{contactType.name}}&cont=1">
Save and continue
</button>
<div class="control">
<a href="/" class="button is-small">Cancel</a>
<button id="place_create_submit" class="button is-small is-link">Upload</button>
<button class="button is-small is-link">Save</button>
</div>
</div>
</form>
Expand All @@ -55,7 +57,4 @@
{% render 'new/place_list.liquid', places: entry, contactType: contactType %}
{% endfor %}
{% endif %}
<div class="p-2">
<a>view history</a>
</div>
</div>
12 changes: 9 additions & 3 deletions src/liquid/new/create_place.liquid
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@
New
{{ contactType.contact_friendly }}
</title>
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
<script src="https://unpkg.com/htmx-ext-sse@2.2.2/sse.js"></script>
<script src="/public/htmx.min.js"></script>
<script src="/public/sse.js"></script>
<link rel="stylesheet" href="/public/bulma.min.css">
<link rel="stylesheet" href="/public/material-symbols.css">
</head>
<body>
<nav class="navbar" role="navigation" aria-label="main navigation"></nav>
<nav class="navbar" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<a class="navbar-item" href="/">
<img src="{{ logo }}">
</a>
</div>
</nav>
<div class="container is-max-desktop px-4" hx-ext="sse" sse-connect="/events/connection">
{% render 'new/create_form.liquid', contactType: contactType, hierarchy: hierarchy, data: data, places: places %}
</div>
Expand Down
7 changes: 7 additions & 0 deletions src/routes/add-place.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import RemotePlaceResolver from '../lib/remote-place-resolver';
import { UploadManager } from '../services/upload-manager';
import RemotePlaceCache from '../lib/remote-place-cache';
import WarningSystem from '../warnings';
import semver from 'semver';

export default async function addPlace(fastify: FastifyInstance) {
fastify.get('/add-place', async (req, resp) => {
Expand All @@ -17,6 +18,12 @@ export default async function addPlace(fastify: FastifyInstance) {
const contactType = queryParams.type
? Config.getContactType(queryParams.type)
: contactTypes[contactTypes.length - 1];

if (semver.gte(req.chtSession.chtCoreVersion, '4.9.0') && contactType.can_assign_multiple) {
resp.redirect(`/new?place_type=${queryParams.type}`);
return;
}

const op = queryParams.op || 'new';
const tmplData = {
view: 'add',
Expand Down
2 changes: 1 addition & 1 deletion src/routes/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export default async function sessionCache(fastify: FastifyInstance) {
const directiveModel = new DirectiveModel(sessionCache, req.cookies.filter);

const chtApi = new ChtApi(req.chtSession);
uploadManager.doUpload(sessionCache.getPlaces(), chtApi, ignoreWarnings === 'true');
uploadManager.doUpload(sessionCache.getPlaces(), chtApi, true);

return resp.view('src/liquid/place/directive.html', {
directiveModel
Expand Down
37 changes: 18 additions & 19 deletions src/routes/new.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { ChtApi } from '../lib/cht-api';
import SessionCache from '../services/session-cache';
import _ from 'lodash';
import PlaceFactory from '../services/place-factory';
import { UploadManager } from '../services/upload-manager';
import Validation from '../validation';
import { PlaceUploadState } from '../services/place';

export default async function newHandler(fastify: FastifyInstance) {

Expand All @@ -15,37 +15,36 @@ export default async function newHandler(fastify: FastifyInstance) {
const sessionCache: SessionCache = req.sessionCache;

const grouped = Object.values(
_.groupBy(sessionCache.getPlaces({ type: contactType.name }), (p) => p.contact.id)
).filter(p => p.length > 0).reverse();
_.groupBy(sessionCache.getPlaces({ type: contactType.name }).filter(p => p.state === PlaceUploadState.STAGED), (p) => p.contact.id)
)
.filter(p => p.length > 0)
.reverse();

const data = {
hierarchy: Config.getHierarchyWithReplacement(contactType, 'desc'),
contactType,
places: grouped
places: grouped,
logo: Config.getLogoBase64()
};

return resp.view('src/liquid/new/create_place.liquid', data);
});

fastify.post('/new', async (req, resp) => {
const { place_type } = req.query as any;
const { place_type, cont } = req.query as any;

const contactType = Config.getContactType(place_type);
const chtApi = new ChtApi(req.chtSession);
const sessionCache: SessionCache = req.sessionCache;
const uploadManager: UploadManager = fastify.uploadManager;

const places = await PlaceFactory.createManyWithSingleUser(req.body as {[key:string]:string}, contactType, sessionCache, chtApi);
uploadManager.uploadWithSingleUser(places, chtApi);

const grouped = Object.values(
_.groupBy(sessionCache.getPlaces({ type: contactType.name }), (p) => p.contact.id)
).filter(p => p.length > 0).reverse();
const data = {
hierarchy: Config.getHierarchyWithReplacement(contactType, 'desc'),
contactType,
places: grouped
};

return resp.view('src/liquid/new/create_form.liquid', data);
await PlaceFactory.createManyWithSingleUser(req.body as {[key:string]:string}, contactType, sessionCache, chtApi);

if (cont) {
resp.header('HX-Redirect', `/new?place_type=${place_type}`);
} else {
resp.header('HX-Redirect', `/`);
}
return;
});

fastify.get('/place_form', async (req, resp) => {
Expand Down
52 changes: 28 additions & 24 deletions src/services/upload-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import EventEmitter from 'events';
import * as RetryLogic from '../lib/retry-logic';
import { ChtApi, CreatedPlaceResult, PlacePayload } from '../lib/cht-api';
import { Config } from '../config';
import Place, { PlaceUploadState } from './place';
import Place, { PlaceUploadState, UserCreationDetails } from './place';
import RemotePlaceCache from '../lib/remote-place-cache';
import { UploadNewPlace } from './upload.new';
import { UploadReplacementWithDeletion } from './upload.replacement';
Expand All @@ -21,20 +21,25 @@ export interface Uploader {
export class UploadManager extends EventEmitter {
doUpload = async (places: Place[], chtApi: ChtApi, ignoreWarnings: boolean = false) => {
const placesNeedingUpload = places.filter(p => {
return !p.isCreated && !p.hasValidationErrors && !p.hasSharedUser && (ignoreWarnings || !p.warnings.length);
return !p.isCreated && !p.hasValidationErrors && (ignoreWarnings || !p.warnings.length);
});
this.eventedPlaceStateChange(placesNeedingUpload, PlaceUploadState.SCHEDULED);

const independants = placesNeedingUpload.filter(p => !p.isDependant);
const dependants = placesNeedingUpload.filter(p => p.isDependant);
const independants = placesNeedingUpload.filter(p => !p.isDependant && !p.hasSharedUser);
const dependants = placesNeedingUpload.filter(p => p.isDependant && !p.hasSharedUser);

await this.uploadPlacesInBatches(independants, chtApi);
await this.uploadPlacesInBatches(dependants, chtApi);
await this.uploadGrouped(placesNeedingUpload, chtApi);
};

uploadWithSingleUser = async (places: Place[], api: ChtApi) => {
uploadGrouped = async (places: Place[], api: ChtApi) => {
const grouped = _.groupBy(places, place => place.contact.id);
Object.keys(grouped).forEach(async k => {
await this.uploadGroup(grouped[k], api);
const places = grouped[k];
if (places.length > 1) {
await this.uploadGroup(places[0].creationDetails, places.slice(1), api);
}
});
};

Expand Down Expand Up @@ -89,34 +94,33 @@ export class UploadManager extends EventEmitter {
}
}

private async uploadGroup(places: Place[], api: ChtApi) {
let contactId;
const placeIds: string[] = [];
for (let i = 0; i < places.length; i++) {
const place = places[i];
private async uploadGroup(creationDetails: UserCreationDetails, places: Place[], api: ChtApi) {
if (!creationDetails.username || !creationDetails.placeId) {
throw new Error('creationDetails must not be empty');
}
const placeIds: {[key:string]: string} = { [creationDetails.placeId]: '' };
for (const place of places) {
this.eventedPlaceStateChange(place, PlaceUploadState.IN_PROGRESS);
const payload = place.asChtPayload(api.chtSession.username, contactId);
const payload = place.asChtPayload(api.chtSession.username, creationDetails.contactId);
await Config.mutate(payload, api, place.isReplacement);
const result = await api.createPlace(payload);
if (!contactId) {
if (!result.contactId) {
throw new Error('expected contact id');
}
contactId = result.contactId;
}

place.creationDetails.contactId = result.contactId;
placeIds.push(result.placeId);
place.creationDetails.placeId = result.placeId;
placeIds[result.placeId] = result.placeId;
}
const userPayload = new UserPayload(places[0], placeIds, contactId!);
const { username, password } = await RetryLogic.createUserWithRetries(userPayload, api);

await api.updateUser({ username: creationDetails.username!, place: Object.keys(placeIds)});
const created_at = new Date().getTime();
places.forEach(place => {
place.creationDetails.username = username;
place.creationDetails.password = password;
place.creationDetails.username = creationDetails.username;
place.creationDetails.password = creationDetails.password;
place.creationDetails.created_at = created_at;

this.eventedPlaceStateChange(place, PlaceUploadState.SUCCESS);
});
this.emit('refresh_grouped', contactId);

this.emit('refresh_grouped', creationDetails.contactId);
}

public triggerRefresh(place_id: string) {
Expand Down

0 comments on commit 542d2c5

Please sign in to comment.