Skip to content

Commit

Permalink
Merge pull request #4974 from kobotoolbox/fix-project-usage-table-dis…
Browse files Browse the repository at this point in the history
…play-values

Billing: Fix project usage table display values and banner
  • Loading branch information
jamesrkiger committed Jun 10, 2024
2 parents c55975a + b8284ec commit c8e4440
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 37 deletions.
7 changes: 6 additions & 1 deletion jsapp/js/account/usage/usage.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,25 @@ export interface AssetUsage {
results: AssetWithUsage[];
}

interface AssetWithUsage {
export interface AssetWithUsage {
asset: string;
uid: string;
asset__name: string;
nlp_usage_current_month: {
total_nlp_asr_seconds: number;
total_nlp_mt_characters: number;
};
nlp_usage_current_year: {
total_nlp_asr_seconds: number;
total_nlp_mt_characters: number;
};
nlp_usage_all_time: {
total_nlp_asr_seconds: number;
total_nlp_mt_characters: number;
};
storage_bytes: number;
submission_count_current_month: number;
submission_count_current_year: number;
submission_count_all_time: number;
deployment_status: string;
}
Expand Down
21 changes: 21 additions & 0 deletions jsapp/js/account/usage/usageProjectBreakdown.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,27 @@
overflow-x: auto;
}

.intervalBanner {
background-color: colors.$kobo-bg-blue;
display: flex;
padding: 0 2%;
align-items: center;
justify-content: space-between;
min-height: 75px;
border-radius: 0.5em;
margin: 2% 0% 2% 0%;
}

.intervalBannerContent {
display: flex;
align-items: center;
flex-basis: 100%;
}

.intervalBannerText {
padding: 2%;
}

.root table {
width: 100%;
border-collapse: collapse;
Expand Down
107 changes: 71 additions & 36 deletions jsapp/js/account/usage/usageProjectBreakdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@ import {ROUTES} from 'jsapp/js/router/routerConstants';
import AssetStatusBadge from 'jsapp/js/components/common/assetStatusBadge';
import LoadingSpinner from 'jsapp/js/components/common/loadingSpinner';
import prettyBytes from 'pretty-bytes';
import type {AssetUsage} from 'js/account/usage/usage.api';
import type {AssetUsage, AssetWithUsage} from 'js/account/usage/usage.api';
import {getAssetUsageForOrganization} from 'js/account/usage/usage.api';
import {USAGE_ASSETS_PER_PAGE} from 'jsapp/js/constants';
import SortableProjectColumnHeader from 'jsapp/js/projects/projectsTable/sortableProjectColumnHeader';
import type {ProjectFieldDefinition} from 'jsapp/js/projects/projectViews/constants';
import type {ProjectsTableOrder} from 'jsapp/js/projects/projectsTable/projectsTable';
import {truncateNumber} from 'jsapp/js/utils';
import {UsageContext, useUsage} from './useUsage.hook';
import Button from 'js/components/common/button';
import Icon from 'js/components/common/icon';
import {OrganizationContext} from 'js/account/organizations/useOrganization.hook';

type ButtonType = 'back' | 'forward';
Expand All @@ -25,6 +28,7 @@ const ProjectBreakdown = () => {
results: [],
});
const [order, setOrder] = useState({});
const [showIntervalBanner, setShowIntervalBanner] = useState(true);
const [loading, setLoading] = useState(true);
const [usage] = useContext(UsageContext);
const [organization] = useContext(OrganizationContext);
Expand Down Expand Up @@ -59,6 +63,10 @@ const ProjectBreakdown = () => {
return <LoadingSpinner />;
}

function dismissIntervalBanner() {
setShowIntervalBanner(false);
}

const calculateRange = (): string => {
const totalProjects = parseInt(projectData.count);
let startRange = (currentPage - 1) * USAGE_ASSETS_PER_PAGE + 1;
Expand Down Expand Up @@ -118,8 +126,68 @@ const ProjectBreakdown = () => {
setOrder(newOrder);
};

const renderProjectRow = (project: AssetWithUsage) => {
const periodSubmissions =
project[
`submission_count_current_${usage.trackingPeriod}`
].toLocaleString();

const periodASRSeconds = truncateNumber(
project[`nlp_usage_current_${usage.trackingPeriod}`]
.total_nlp_asr_seconds / 60,
1
).toLocaleString();

const periodMTCharacters =
project[
`nlp_usage_current_${usage.trackingPeriod}`
].total_nlp_mt_characters.toLocaleString();

return (
<tr key={project.asset}>
<td>
<Link
className={styles.link}
to={ROUTES.FORM_SUMMARY.replace(':uid', project.uid)}
>
{project.asset__name}
</Link>
</td>
<td>{project.submission_count_all_time.toLocaleString()}</td>
<td className={styles.currentMonth}>
{periodSubmissions}
</td>
<td>{prettyBytes(project.storage_bytes)}</td>
<td>{periodASRSeconds}</td>
<td>{periodMTCharacters}</td>
<td className={styles.badge}>
<AssetStatusBadge deploymentStatus={project.deployment_status} />
</td>
</tr>
);
};

return (
<div className={styles.root}>
{showIntervalBanner && (
<div className={styles.intervalBanner}>
<div className={styles.intervalBannerContent}>
<Icon name={'information'} size='m' color='blue' />
<div className={styles.intervalBannerText}>
{t(
'Submissions, transcription minutes, and translation characters reflect usage for the current ##INTERVAL## based on your plan settings.'
).replace('##INTERVAL##', usage.trackingPeriod)}
</div>
</div>
<Button
color='storm'
size='s'
type='bare'
startIcon='close'
onClick={dismissIntervalBanner}
/>
</div>
)}
<table>
<thead className={styles.headerFont}>
<tr>
Expand All @@ -133,11 +201,7 @@ const ProjectBreakdown = () => {
/>
</th>
<th>{t('Submissions (Total)')}</th>
<th>
{usage.trackingPeriod === 'year'
? t('Submissions (This year)')
: t('Submissions (This month)')}
</th>
<th>{t('Submissions')}</th>
<th>{t('Data storage')}</th>
<th>{t('Transcript minutes')}</th>
<th>{t('Translation characters')}</th>
Expand All @@ -164,36 +228,7 @@ const ProjectBreakdown = () => {
</tbody>
) : (
<tbody>
{projectData.results.map((project) => (
<tr key={project.asset}>
<td>
<Link
className={styles.link}
to={ROUTES.FORM_SUMMARY.replace(':uid', project.uid)}
>
{project.asset__name}
</Link>
</td>
<td>{project.submission_count_all_time.toLocaleString()}</td>
<td className={styles.currentMonth}>
{project.submission_count_current_month.toLocaleString()}
</td>
<td>{prettyBytes(project.storage_bytes)}</td>
<td>
{(project.nlp_usage_current_month.total_nlp_asr_seconds / 60)
.toFixed(1)
.toLocaleString()}
</td>
<td>
{project.nlp_usage_current_month.total_nlp_mt_characters.toLocaleString()}
</td>
<td className={styles.badge}>
<AssetStatusBadge
deploymentStatus={project.deployment_status}
/>
</td>
</tr>
))}
{projectData.results.map((project) => renderProjectRow(project))}
</tbody>
)}
</table>
Expand Down

0 comments on commit c8e4440

Please sign in to comment.