Skip to content

Commit

Permalink
Merge pull request #4942 from kobotoolbox/typescriptize-data-table-co…
Browse files Browse the repository at this point in the history
…mponent

Typescriptize DataTable component and more
  • Loading branch information
p2edwards committed Jun 18, 2024
2 parents 2160052 + de379aa commit 275be43
Show file tree
Hide file tree
Showing 64 changed files with 2,512 additions and 1,855 deletions.
12 changes: 6 additions & 6 deletions jsapp/js/account/security/mfa/mfaSection.component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import ToggleSwitch from 'js/components/common/toggleSwitch';
import Icon from 'js/components/common/icon';
import InlineMessage from 'js/components/common/inlineMessage';
import LoadingSpinner from 'js/components/common/loadingSpinner';
import {stores} from 'js/stores';
import type {
MfaUserMethodsResponse,
MfaActivatedResponse,
Expand All @@ -15,6 +14,7 @@ import {MODAL_TYPES} from 'jsapp/js/constants';
import envStore from 'js/envStore';
import './mfaSection.scss';
import {formatTime, formatDate} from 'js/utils';
import pageState from 'js/pageState.store';

bem.SecurityRow = makeBem(null, 'security-row');
bem.SecurityRow__header = makeBem(bem.SecurityRow, 'header');
Expand Down Expand Up @@ -70,7 +70,7 @@ export default class SecurityRoute extends React.Component<{}, SecurityState> {
),
mfaActions.activate.completed.listen(this.mfaActivating.bind(this)),
mfaActions.confirmCode.completed.listen(this.mfaActivated.bind(this)),
mfaActions.deactivate.completed.listen(this.mfaDeactivated.bind(this)),
mfaActions.deactivate.completed.listen(this.mfaDeactivated.bind(this))
);

mfaActions.getUserMethods();
Expand Down Expand Up @@ -103,7 +103,7 @@ export default class SecurityRoute extends React.Component<{}, SecurityState> {

mfaActivating(response: MfaActivatedResponse) {
if (response && !response.inModal) {
stores.pageState.showModal({
pageState.showModal({
type: MODAL_TYPES.MFA_MODALS,
qrCode: response.details,
modalType: 'qr',
Expand Down Expand Up @@ -133,7 +133,7 @@ export default class SecurityRoute extends React.Component<{}, SecurityState> {
if (isActive) {
mfaActions.activate();
} else {
stores.pageState.showModal({
pageState.showModal({
type: MODAL_TYPES.MFA_MODALS,
modalType: 'deactivate',
customModalHeader: this.renderCustomHeader(),
Expand All @@ -149,7 +149,7 @@ export default class SecurityRoute extends React.Component<{}, SecurityState> {
) {
evt.preventDefault();

stores.pageState.showModal({
pageState.showModal({
type: MODAL_TYPES.MFA_MODALS,
modalType: type,
customModalHeader: this.renderCustomHeader(),
Expand Down Expand Up @@ -202,7 +202,7 @@ export default class SecurityRoute extends React.Component<{}, SecurityState> {
/>
</bem.SecurityRow__buttons>
</bem.SecurityRow__switch>
</bem.SecurityRow__header>
</bem.SecurityRow__header>

{this.state.isMfaActive && this.state.isMfaAvailable && (
<bem.MFAOptions>
Expand Down
59 changes: 54 additions & 5 deletions jsapp/js/actions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@ interface GetSubmissionCompletedDefinition extends Function {
listen: (callback: (response: SubmissionResponse) => void) => Function;
}

interface GetSubmissionsDefinition extends Function {
(options: GetSubmissionsOptions): void;
completed: GetSubmissionsCompletedDefinition;
failed: GenericFailedDefinition;
}

interface GetSubmissionsCompletedDefinition extends Function {
(response: PaginatedResponse<SubmissionResponse>, options: GetSubmissionsOptions): void;
listen: (callback: (response: PaginatedResponse<SubmissionResponse>, options: GetSubmissionsOptions) => void) => Function;
}

interface GetProcessingSubmissionsDefinition extends Function {
(assetUid: string, questionsPaths: string[]): void;
completed: GetProcessingSubmissionsCompletedDefinition;
Expand Down Expand Up @@ -89,6 +100,44 @@ interface GetExportCompletedDefinition extends Function {
listen: (callback: (response: any) => void) => Function;
}

interface TableUpdateSettingsDefinition extends Function {
(assetUid: string, newSettings: object): void;
completed: GenericCallbackDefinition;
failed: GenericFailedDefinition;
}

interface UpdateSubmissionValidationStatusDefinition extends Function {
(
assetUid: string,
submissionUid: string,
data: {'validation_status.uid': ValidationStatus}
): void;
completed: AnySubmissionValidationStatusCompletedDefinition;
failed: GenericFailedDefinition;
}

interface AnySubmissionValidationStatusCompletedDefinition extends Function {
(result: ValidationStatusResponse, sid: string): void;
listen: (callback: (result: ValidationStatusResponse, sid: string) => void) => Function;
}

interface RemoveSubmissionValidationStatusDefinition extends Function {
(assetUid: string, submissionUid: string): void;
completed: AnySubmissionValidationStatusCompletedDefinition;
failed: GenericFailedDefinition;
}

interface DuplicateSubmissionDefinition extends Function {
(assetUid: string, submissionUid: string, data: SubmissionResponse): void;
completed: DuplicateSubmissionCompletedDefinition;
failed: GenericFailedDefinition;
}

interface DuplicateSubmissionCompletedDefinition extends Function {
(assetUid: string, submissionUid: string, duplicatedSubmission: SubmissionResponse): void;
listen: (callback: (assetUid: string, submissionUid: string, duplicatedSubmission: SubmissionResponse) => void) => Function;
}

// NOTE: as you use more actions in your ts files, please extend this namespace,
// for now we are defining only the ones we need.
export namespace actions {
Expand All @@ -114,18 +163,18 @@ export namespace actions {
listTags: GenericDefinition;
createResource: GenericDefinition;
updateAsset: UpdateAssetDefinition;
updateSubmissionValidationStatus: GenericDefinition;
removeSubmissionValidationStatus: GenericDefinition;
updateSubmissionValidationStatus: UpdateSubmissionValidationStatusDefinition;
removeSubmissionValidationStatus: RemoveSubmissionValidationStatusDefinition;
deleteSubmission: GenericDefinition;
duplicateSubmission: GenericDefinition;
duplicateSubmission: DuplicateSubmissionDefinition;
refreshTableSubmissions: GenericDefinition;
getAssetFiles: GenericDefinition;
};
const hooks: object;
const misc: object;
const reports: object;
const table: {
updateSettings: (assetUid: string, newSettings: object) => void;
updateSettings: TableUpdateSettingsDefinition;
};
const map: object;
const permissions: {
Expand All @@ -145,7 +194,7 @@ export namespace actions {
const submissions: {
getSubmission: GetSubmissionDefinition;
getSubmissionByUuid: GetSubmissionDefinition;
getSubmissions: GenericDefinition;
getSubmissions: GetSubmissionsDefinition;
getProcessingSubmissions: GetProcessingSubmissionsDefinition;
bulkDeleteStatus: GenericDefinition;
bulkPatchStatus: GenericDefinition;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,15 @@

import Reflux from 'reflux';
import {dataInterface} from 'js/dataInterface';
import {notify} from 'utils';
import {notify} from 'js/utils';
import {ROOT_URL} from 'js/constants';
import type {
GetSubmissionsOptions,
PaginatedResponse,
SubmissionResponse,
FailResponse,
BulkSubmissionsRequest,
} from 'js/dataInterface';

const submissionsActions = Reflux.createActions({
getSubmission: {children: ['completed', 'failed']},
Expand All @@ -18,25 +25,11 @@ const submissionsActions = Reflux.createActions({
getProcessingSubmissions: {children: ['completed', 'failed']},
});

/**
* @typedef SortObj
* @param {string} id - column name
* @param {boolean} desc - `true` for descending and `false` for ascending
*/

/**
* NOTE: all of the parameters have their default values defined for
* `dataInterface` function.
*
* @param {object} options
* @param {string} options.uid - the asset uid
* @param {number} [options.pageSize]
* @param {number} [options.page]
* @param {SortObj[]} [options.sort]
* @param {string[]} [options.fields]
* @param {string} [options.filter]
*/
submissionsActions.getSubmissions.listen((options) => {
submissionsActions.getSubmissions.listen((options: GetSubmissionsOptions) => {
dataInterface.getSubmissions(
options.uid,
options.pageSize,
Expand All @@ -45,21 +38,20 @@ submissionsActions.getSubmissions.listen((options) => {
options.fields,
options.filter
)
.done((response) => {
.done((response: PaginatedResponse<SubmissionResponse>) => {
submissionsActions.getSubmissions.completed(response, options);
})
.fail((response) => {
.fail((response: FailResponse) => {
submissionsActions.getSubmissions.failed(response, options);
});
});

/**
* This gets an array of submission uuids
* @param {string} assetUid
*/
submissionsActions.getProcessingSubmissions.listen((assetUid, questionsPaths) => {
submissionsActions.getProcessingSubmissions.listen((assetUid: string, questionsPaths: string[]) => {
let fields = '';
questionsPaths.forEach((questionPath) => {
questionsPaths.forEach((questionPath: string) => {
fields += `,"${questionPath}"`;
});

Expand All @@ -75,33 +67,32 @@ submissionsActions.getProcessingSubmissions.failed.listen(() => {
notify(t('Failed to get submissions uuids.'), 'error');
});

submissionsActions.getSubmission.listen((assetUid, submissionId) => {
submissionsActions.getSubmission.listen((assetUid: string, submissionId: string) => {
dataInterface.getSubmission(assetUid, submissionId)
.done(submissionsActions.getSubmission.completed)
.fail(submissionsActions.getSubmission.failed);
});

// There is no shortcut endpoint to get submission using uuid, so we have to
// make a queried call over all submissions.
submissionsActions.getSubmissionByUuid.listen((assetUid, submissionUuid) => {
submissionsActions.getSubmissionByUuid.listen((assetUid: string, submissionUuid: string) => {
// `_uuid` is the legacy identifier that changes (per OpenRosa spec) after every edit;
// `meta/rootUuid` remains consistent across edits.
let query = {
const query = JSON.stringify({
'$or': [
{'meta/rootUuid': submissionUuid},
{'_uuid': submissionUuid},
],
};
query = JSON.stringify(query);
});
$.ajax({
dataType: 'json',
method: 'GET',
url: `${ROOT_URL}/api/v2/assets/${assetUid}/data/?query=${query}`,
})
.done((response) => {
.done((response: PaginatedResponse<SubmissionResponse>) => {
// preferentially return a result matching the persistent UUID
submissionsActions.getSubmissionByUuid.completed(
response.results.find((e) => e['meta/rootUuid'] === submissionUuid) ||
response.results.find((sub) => sub['meta/rootUuid'] === submissionUuid) ||
response.results[0]
);
})
Expand All @@ -111,7 +102,7 @@ submissionsActions.getSubmissionByUuid.failed.listen(() => {
notify(t('Failed to get submission.'), 'error');
});

submissionsActions.bulkDeleteStatus.listen((uid, data) => {
submissionsActions.bulkDeleteStatus.listen((uid: string, data: BulkSubmissionsRequest) => {
dataInterface.bulkRemoveSubmissionsValidationStatus(uid, data)
.done(submissionsActions.bulkDeleteStatus.completed)
.fail(submissionsActions.bulkDeleteStatus.failed);
Expand All @@ -120,7 +111,7 @@ submissionsActions.bulkDeleteStatus.failed.listen(() => {
notify(t('Failed to update submissions.'), 'error');
});

submissionsActions.bulkPatchStatus.listen((uid, data) => {
submissionsActions.bulkPatchStatus.listen((uid: string, data: BulkSubmissionsRequest) => {
dataInterface.bulkPatchSubmissionsValidationStatus(uid, data)
.done(submissionsActions.bulkPatchStatus.completed)
.fail(submissionsActions.bulkPatchStatus.failed);
Expand All @@ -135,12 +126,12 @@ submissionsActions.bulkPatchStatus.failed.listen(() => {
* @param {object} data
* @param {string} data.<field_name_to_update> - with a new value, repeat with different fields if necessary
*/
submissionsActions.bulkPatchValues.listen((uid, submissionIds, data) => {
submissionsActions.bulkPatchValues.listen((uid: string, submissionIds: string[], data: BulkSubmissionsRequest) => {
dataInterface.bulkPatchSubmissionsValues(uid, submissionIds, data)
.done(submissionsActions.bulkPatchValues.completed)
.fail(submissionsActions.bulkPatchValues.failed);
});
submissionsActions.bulkPatchValues.completed.listen((response) => {
submissionsActions.bulkPatchValues.completed.listen((response: {failures: number}) => {
if (response.failures !== 0) {
notify(t('Failed to update some submissions values.'), 'error');
}
Expand All @@ -149,7 +140,7 @@ submissionsActions.bulkPatchValues.failed.listen(() => {
notify(t('Failed to update submissions values.'), 'error');
});

submissionsActions.bulkDelete.listen((uid, data) => {
submissionsActions.bulkDelete.listen((uid: string, data: BulkSubmissionsRequest) => {
dataInterface.bulkDeleteSubmissions(uid, data)
.done(submissionsActions.bulkDelete.completed)
.fail(submissionsActions.bulkDelete.failed);
Expand Down
9 changes: 5 additions & 4 deletions jsapp/js/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,13 @@ import {
isTOSAgreementRouteBlockerActive,
} from 'js/router/routerUtils';
import {isAnyProcessingRouteActive} from 'js/components/processing/routes.utils';
import pageState from 'js/pageState.store';

class App extends React.Component {
constructor(props) {
super(props);
this.state = Object.assign({
pageState: stores.pageState.state,
pageState: pageState.state,
});
}

Expand All @@ -45,12 +46,12 @@ class App extends React.Component {
onRouteChange() {
// slide out drawer overlay on every page change (better mobile experience)
if (this.state.pageState.showFixedDrawer) {
stores.pageState.setState({showFixedDrawer: false});
pageState.setState({showFixedDrawer: false});
}

// hide modal on every page change
if (this.state.pageState.modal) {
stores.pageState.hideModal();
pageState.hideModal();
}
}

Expand Down Expand Up @@ -148,7 +149,7 @@ class App extends React.Component {

App.contextTypes = {router: PropTypes.object};

reactMixin(App.prototype, Reflux.connect(stores.pageState, 'pageState'));
reactMixin(App.prototype, Reflux.connect(pageState, 'pageState'));
reactMixin(App.prototype, mixins.contextRouter);

export default withRouter(App);
Loading

0 comments on commit 275be43

Please sign in to comment.