diff --git a/jsapp/js/account/security/accessLogs/accessLogsSection.component.tsx b/jsapp/js/account/security/accessLogs/accessLogsSection.component.tsx index 096f0f6b0d..eeaed7cde3 100644 --- a/jsapp/js/account/security/accessLogs/accessLogsSection.component.tsx +++ b/jsapp/js/account/security/accessLogs/accessLogsSection.component.tsx @@ -2,21 +2,21 @@ import React from 'react'; // Partial components -import Button from 'js/components/common/button'; +// import Button from 'js/components/common/button'; import PaginatedQueryUniversalTable from 'js/universalTable/paginatedQueryUniversalTable.component'; // Utilities import useAccessLogsQuery, {type AccessLog} from 'js/query/queries/accessLogs.query'; import {formatTime} from 'js/utils'; -import sessionStore from 'js/stores/session'; +// import sessionStore from 'js/stores/session'; // Styles import securityStyles from 'js/account/security/securityRoute.module.scss'; export default function AccessLogsSection() { - function logOutAllSessions() { - sessionStore.logOutAll(); - } + // function logOutAllSessions() { + // sessionStore.logOutAll(); + // } return ( <> @@ -43,11 +43,21 @@ export default function AccessLogsSection() { columns={[ // The `key`s of these columns are matching the `AccessLog` interface // properties (from `accessLogs.query.ts` file) using dot notation. - {key: 'metadata.source', label: t('Source')}, + { + key: 'metadata.source', + label: t('Source'), + cellFormatter: (log: AccessLog) => { + if (log.metadata.auth_type === 'submission-group') { + return t('Data Submissions (##count##)').replace('##count##', String(log.count)); + } else { + return log.metadata.source; + } + }, + }, { key: 'date_created', label: t('Last activity'), - cellFormatter: (date: string) => formatTime(date), + cellFormatter: (log: AccessLog) => formatTime(log.date_created), }, {key: 'metadata.ip_address', label: t('IP Address')}, ]} diff --git a/jsapp/js/projects/projectViews/viewSwitcher.module.scss b/jsapp/js/projects/projectViews/viewSwitcher.module.scss index c37aaad366..e4cc0a641b 100644 --- a/jsapp/js/projects/projectViews/viewSwitcher.module.scss +++ b/jsapp/js/projects/projectViews/viewSwitcher.module.scss @@ -22,7 +22,9 @@ font-size: sizes.$x20; font-weight: 800; color: colors.$kobo-gray-800; - padding: sizes.$x6 sizes.$x16; + padding: 2px sizes.$x16; + // we want it to be 32px height + line-height: 28px; max-width: sizes.$x400; :global { diff --git a/jsapp/js/query/queries/accessLogs.query.ts b/jsapp/js/query/queries/accessLogs.query.ts index ea2bf8df1d..dfa7bea64e 100644 --- a/jsapp/js/query/queries/accessLogs.query.ts +++ b/jsapp/js/query/queries/accessLogs.query.ts @@ -4,23 +4,21 @@ import type {PaginatedResponse} from 'js/dataInterface'; import {fetchGet} from 'js/api'; export interface AccessLog { - app_label: 'kobo_auth' | string; - model_name: 'User' | string; - object_id: number; /** User URL */ user: string; user_uid: string; + /** Date string */ + date_created: string; username: string; - action: 'auth' | string; metadata: { + auth_type: 'digest' | 'submission-group' | string; + // Both `source` and `ip_address` appear only for `digest` type /** E.g. "Firefox (Ubuntu)" */ - source: string; - auth_type: 'Digest' | string; - ip_address: string; + source?: string; + ip_address?: string; }; - /** Date string */ - date_created: string; - log_type: 'access' | string; + /** For `submission-group` type, here is the number of submisssions. */ + count: number; } async function getAccessLogs(limit: number, offset: number) { diff --git a/jsapp/js/universalTable/paginatedQueryUniversalTable.component.tsx b/jsapp/js/universalTable/paginatedQueryUniversalTable.component.tsx index 167d49059c..595ffe9ad9 100644 --- a/jsapp/js/universalTable/paginatedQueryUniversalTable.component.tsx +++ b/jsapp/js/universalTable/paginatedQueryUniversalTable.component.tsx @@ -18,7 +18,7 @@ interface PaginatedQueryUniversalTableProps { // Below are props from `UniversalTable` that should come from the parent // component (these are kind of "configuration" props). The other // `UniversalTable` props are being handled here internally. - columns: UniversalTableColumn[]; + columns: UniversalTableColumn[]; } const PAGE_SIZES = [10, 30, 50, 100]; diff --git a/jsapp/js/universalTable/universalTable.component.tsx b/jsapp/js/universalTable/universalTable.component.tsx index eafe0c980d..23fdb007bb 100644 --- a/jsapp/js/universalTable/universalTable.component.tsx +++ b/jsapp/js/universalTable/universalTable.component.tsx @@ -21,7 +21,7 @@ import {generateUuid} from 'js/utils'; // Styles import styles from './universalTable.module.scss'; -export interface UniversalTableColumn { +export interface UniversalTableColumn { /** * Pairs to data object properties. It is using dot notation, so it's possible * to match data from a nested object :ok:. @@ -40,14 +40,15 @@ export interface UniversalTableColumn { size?: number; /** * This is an optional formatter function that will be used when rendering - * the cell value. Without it a literal text value will be rendered. + * the cell value. Without it a literal text value will be rendered. For more + * flexibility, function receives whole original data object. */ - cellFormatter?: (value: string) => React.ReactNode; + cellFormatter?: (value: DataItem) => React.ReactNode; } interface UniversalTableProps { /** A list of column definitions */ - columns: UniversalTableColumn[]; + columns: UniversalTableColumn[]; data: DataItem[]; // PAGINATION // To see footer with pagination you need to pass all these below: @@ -133,7 +134,7 @@ export default function UniversalTable( header: () => columnDef.label, cell: (cellProps: CellContext) => { if (columnDef.cellFormatter) { - return columnDef.cellFormatter(cellProps.getValue()); + return columnDef.cellFormatter(cellProps.row.original); } else { return cellProps.renderValue(); }