Skip to content
Merged
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
2 changes: 2 additions & 0 deletions src/platform/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,7 @@ NEXT_PUBLIC_PAYMENTS_STANDARD_PRICE_ID=
PAYMENTS_STANDARD_PRICE_ID=
NEXT_PUBLIC_PAYMENTS_PREMIUM_PRICE_ID=
PAYMENTS_PREMIUM_PRICE_ID=
NEXT_PUBLIC_PAYMENT_CLIENT_TOKEN=
NEXT_PUBLIC_PAYMENT_ENVIRONMENT=


2 changes: 2 additions & 0 deletions src/platform/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ COPY --from=builder /app/package.json ./package.json
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs

Expand Down
2 changes: 1 addition & 1 deletion src/platform/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"scripts": {
"dev": "next dev",
"build": "next build",
"postbuild": "node scripts/copy-standalone-assets.mjs",
"postbuild": "node -e \"const fs=require('node:fs'); const path=require('node:path'); const projectRoot=process.cwd(); const standaloneRoot=path.join(projectRoot,'.next','standalone'); const publicSource=path.join(projectRoot,'public'); const publicTarget=path.join(standaloneRoot,'public'); const staticSource=path.join(projectRoot,'.next','static'); const staticTarget=path.join(standaloneRoot,'.next','static'); const copyDirectory=(source,target)=>{ if(!fs.existsSync(source)) return false; fs.mkdirSync(path.dirname(target),{recursive:true}); fs.rmSync(target,{recursive:true,force:true}); fs.cpSync(source,target,{recursive:true}); return true; }; if(!fs.existsSync(standaloneRoot)){ throw new Error(`Standalone output not found at ${standaloneRoot}. Run 'yarn build' first.`); } copyDirectory(publicSource, publicTarget); copyDirectory(staticSource, staticTarget);\"",
"start": "node -e \"const { loadEnvConfig } = require('@next/env'); loadEnvConfig(process.cwd()); process.env.HOSTNAME='localhost'; require('./.next/standalone/server.js')\"",
"lint": "next lint",
"format": "prettier --write .",
Expand Down
29 changes: 0 additions & 29 deletions src/platform/scripts/copy-standalone-assets.mjs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { useEffect, useState, type FormEvent } from 'react';
import AuthLayout from '@/shared/layouts/AuthLayout';
import SocialAuthSection from '@/shared/components/auth/SocialAuthSection';
import SelectedEmailCard from '@/shared/components/auth/SelectedEmailCard';
import { signIn } from 'next-auth/react';
import Link from 'next/link';
import { useSearchParams } from 'next/navigation';
Expand Down Expand Up @@ -201,27 +202,7 @@ export default function LoginPage() {
</form>
) : (
<form onSubmit={handleSubmit(onSubmit)} className="w-full space-y-4">
<div className="rounded-xl border border-gray-200 bg-gray-50/80 px-3 py-3 shadow-sm dark:border-gray-700 dark:bg-gray-800/50 sm:px-4">
<div className="flex items-center justify-between gap-3">
<div className="min-w-0">
<p className="text-[10px] font-semibold uppercase tracking-[0.22em] text-gray-500 dark:text-gray-400">
Account
</p>
<p className="mt-0.5 truncate text-sm font-medium text-gray-900 dark:text-white">
{emailValue || 'your email address'}
</p>
</div>

<Button
type="button"
variant="text"
size="sm"
onClick={handleGoBack}
>
Change
</Button>
</div>
</div>
<SelectedEmailCard email={emailValue} onChangeEmail={handleGoBack} />

<Input
label="Password"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { useEffect, useState, type FormEvent } from 'react';
import { useParams } from 'next/navigation';
import AuthLayout from '@/shared/layouts/AuthLayout';
import SocialAuthSection from '@/shared/components/auth/SocialAuthSection';
import SelectedEmailCard from '@/shared/components/auth/SelectedEmailCard';
import { signIn } from 'next-auth/react';
import Link from 'next/link';
import { useSearchParams } from 'next/navigation';
Expand Down Expand Up @@ -191,27 +192,7 @@ export default function OrgLoginPage() {
</form>
) : (
<form onSubmit={handleSubmit(onSubmit)} className="w-full space-y-4">
<div className="rounded-xl border border-gray-200 bg-gray-50/80 px-3 py-3 shadow-sm dark:border-gray-700 dark:bg-gray-800/50 sm:px-4">
<div className="flex items-center justify-between gap-3">
<div className="min-w-0">
<p className="text-[10px] font-semibold uppercase tracking-[0.22em] text-gray-500 dark:text-gray-400">
Account
</p>
<p className="mt-0.5 truncate text-sm font-medium text-gray-900 dark:text-white">
{emailValue || 'your email address'}
</p>
</div>

<Button
type="button"
variant="text"
size="sm"
onClick={handleGoBack}
>
Change
</Button>
</div>
</div>
<SelectedEmailCard email={emailValue} onChangeEmail={handleGoBack} />

<Input
label="Password"
Expand Down
2 changes: 2 additions & 0 deletions src/platform/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Inter } from 'next/font/google';
import NextTopLoader from 'nextjs-toploader';
import { ReduxProvider } from '@/shared/providers/redux-provider';
import { AuthProvider } from '@/shared/providers/auth-provider';
import PaddleProvider from '@/shared/providers/paddle-provider';
import { PostHogProvider } from '@/shared/providers/posthog-provider';
import { GoogleAnalyticsProvider } from '@/shared/providers/google-analytics-provider';
import { Toaster } from '@/shared/components/ui';
Expand Down Expand Up @@ -52,6 +53,7 @@ export default function RootLayout({
<ReduxProvider>
<ErrorBoundary>
<AuthProvider>
<PaddleProvider />
<GoogleAnalyticsProvider>
<PostHogProvider>
<AppNetworkGate>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import type { PollutantType } from '@/shared/components/charts/types';
import { useResizeObserver } from '@/shared/hooks';
import { AqWind01 } from '@airqo/icons-react';
import { anonymizeSiteData, trackEvent } from '@/shared/utils/analytics';
import { getSiteDisplayName } from '@/shared/utils/siteUtils';

export const AnalyticsCard: React.FC<AnalyticsCardProps> = memo(
({ siteData, className, showIcon = true, selectedPollutant, onClick }) => {
Expand Down Expand Up @@ -45,7 +46,6 @@ export const AnalyticsCard: React.FC<AnalyticsCardProps> = memo(
useResizeObserver(locationRef.current, checkTruncation);

const {
name,
location,
value,
status: rawStatus,
Expand All @@ -69,8 +69,7 @@ export const AnalyticsCard: React.FC<AnalyticsCardProps> = memo(
// Use selected pollutant for display, fallback to site data pollutant
const displayPollutant = selectedPollutant || (pollutant as PollutantType);

// Format name for display by replacing underscores with spaces
const displayName = name.replace(/_/g, ' ');
const displayName = getSiteDisplayName(siteData);

// Get appropriate icons
// Narrow types before indexing into the icon maps to satisfy TS
Expand Down Expand Up @@ -142,8 +141,8 @@ export const AnalyticsCard: React.FC<AnalyticsCardProps> = memo(
<Tooltip
content={
percentageDifference !== undefined
? `Air quality ${percentageDifference > 0 ? 'worsened' : 'improved'} by ${Math.abs(percentageDifference).toFixed(1)}% compared to last week.`
: 'Air quality trend compared to last week.'
? `Air quality ${percentageDifference > 0 ? 'worsened' : 'improved'} by ${Math.abs(percentageDifference).toFixed(1)}% compared with the previous reading.`
: 'Air quality trend compared with the previous reading.'
}
className="bg-black"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import type { SiteData } from '../types';
import { openMoreInsights } from '@/shared/store/insightsSlice';
import type { NormalizedChartData } from '@/shared/components/charts/types';
import { trackEvent } from '@/shared/utils/analytics';
import { getSiteDisplayName } from '@/shared/utils/siteUtils';
import {
useActiveGroupCohorts,
useCohort,
Expand Down Expand Up @@ -112,6 +113,7 @@ export const AnalyticsDashboard: React.FC<AnalyticsDashboardProps> = ({
siteCards,
isLoading: siteCardsLoading,
isRefreshing: siteCardsRefreshing,
error: siteCardsError,
refetch: refreshSiteCards,
} = useAnalyticsSiteCards({
selectedSiteIds,
Expand Down Expand Up @@ -194,15 +196,11 @@ export const AnalyticsDashboard: React.FC<AnalyticsDashboardProps> = ({
return Array.from(uniqueSiteIds)
.map(siteId => {
const siteData = selectedSites.find(site => site._id === siteId);
const displayName = getSiteDisplayName(siteData || {});
return {
_id: siteId,
name:
siteData?.search_name ||
siteData?.name ||
siteData?.formatted_name ||
siteData?.generated_name ||
'Unknown Site',
search_name: siteData?.search_name,
name: displayName,
search_name: displayName,
country: siteData?.country,
};
})
Expand Down Expand Up @@ -276,13 +274,8 @@ export const AnalyticsDashboard: React.FC<AnalyticsDashboardProps> = ({
sites ||
selectedSites.map(site => ({
_id: site._id,
name:
site.search_name ||
site.name ||
site.formatted_name ||
site.generated_name ||
'Unknown Site',
search_name: site.search_name || site.name,
name: getSiteDisplayName(site),
search_name: getSiteDisplayName(site),
country: site.country,
}));

Expand All @@ -291,10 +284,11 @@ export const AnalyticsDashboard: React.FC<AnalyticsDashboardProps> = ({

// Handle individual card click - open more insights for specific site
const handleCardClick = (siteData: SiteData) => {
const displayName = getSiteDisplayName(siteData);
const selectedSite = {
_id: siteData._id,
name: siteData.search_name || siteData.name,
search_name: siteData.search_name || siteData.name,
name: displayName,
search_name: displayName,
country: siteData.location,
};

Expand Down Expand Up @@ -457,10 +451,12 @@ export const AnalyticsDashboard: React.FC<AnalyticsDashboardProps> = ({
sites={siteCards}
onManageFavorites={handleManageFavorites}
onRefresh={handleRefreshDashboard}
isRefreshing={isRefreshing}
isRefreshing={siteCardsRefreshing || isRefreshing}
selectedPollutant={filters.pollutant}
onCardClick={handleCardClick}
isLoading={siteCardsLoading || siteCardsRefreshing || isRefreshing}
isLoading={siteCardsLoading}
placeholderCount={selectedSites.length}
errorMessage={siteCardsError}
showIcon={showIcons}
onShowIconsChange={setShowIcons}
infoLine={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
FREQUENCY_LABELS,
POLLUTANT_LABELS,
} from '@/shared/components/charts/constants';
import { getSiteDisplayName } from '@/shared/utils/siteUtils';

interface DownloadDialogProps {
isOpen: boolean;
Expand Down Expand Up @@ -180,12 +181,7 @@ export const DownloadDialog: React.FC<DownloadDialogProps> = ({
() =>
selectedSites.map(site => ({
id: site._id,
displayName:
site.search_name ||
site.name ||
site.formatted_name ||
site.generated_name ||
`Site ${site._id.slice(-6)}`,
displayName: getSiteDisplayName(site),
location: site.country || site.region || site.city || '',
})),
[selectedSites]
Expand Down
Loading
Loading