Skip to content
Open
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: 1 addition & 1 deletion front/app/(comprendre)/methodologie/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export default function Page() {
<li>l'indice de transparence des marchés publics</li>
<li>l'indice de transparence agrégé des 2 indices précédents</li>
</ul>
<h3 className='mb-4 mt-10'>Calcul de l'indice de transparence des subventions</h3>
<h3 className='mb-4 mt-10'>Calcul de l'indice de transparence des marchés publics</h3>
<p className='font-bold'>
L'indice de transparence des marchés publics est établi selon la conjonction de 3
facteurs principaux&nbsp;:
Expand Down
4 changes: 2 additions & 2 deletions front/app/(visualiser)/interpeller/[siren]/step3/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ async function getCommunity(siren: string) {
export default async function InterpellateStep3({ params }: CommunityPageProps) {
const { siren } = await params;
const community = await getCommunity(siren);
const { type, nom } = community;
const { formattedType, nom } = community;
const interpellateCommunityTemplateHtml = getEmailTemplate('interpellate-community');

return (
Expand All @@ -37,7 +37,7 @@ export default async function InterpellateStep3({ params }: CommunityPageProps)

<InterpellateForm
htmlTemplate={interpellateCommunityTemplateHtml}
communityType={type}
communityType={formattedType}
communityName={nom}
siren={siren}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { NextRequest, NextResponse } from 'next/server';

import { fetchMarchesPublicsComparison } from '#utils/fetchers/marches-publics/fetchMarchesPublicsComparison-server';
import { ScopeType } from '#utils/types';

export async function GET(
request: NextRequest,
Expand All @@ -9,13 +10,13 @@ export async function GET(
try {
const { siren } = await params;
const { searchParams } = new URL(request.url);
const scope = searchParams.get('scope') || 'régional';
const scopeType = searchParams.get('scope') as ScopeType || ScopeType.Region;

if (siren === undefined) {
throw new Error('Siren is not defined');
}

const data = await fetchMarchesPublicsComparison(siren, scope);
const data = await fetchMarchesPublicsComparison(siren, scopeType);

return NextResponse.json(data);
} catch (error) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { NextRequest, NextResponse } from 'next/server';

import { fetchSubventionsComparison } from '#utils/fetchers/subventions/fetchSubventionsComparison-server';
import { ScopeType } from '#utils/types';

export async function GET(
request: NextRequest,
Expand All @@ -9,13 +10,13 @@ export async function GET(
try {
const { siren } = await params;
const { searchParams } = new URL(request.url);
const scope = searchParams.get('scope') || 'régional';
const scopeType = searchParams.get('scope') as ScopeType || ScopeType.Region;

if (siren === undefined) {
throw new Error('Siren is not defined');
}

const data = await fetchSubventionsComparison(siren, scope);
const data = await fetchSubventionsComparison(siren, scopeType);

return NextResponse.json(data);
} catch (error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ function MobileHeaderCard({
{/* Section Budget Total */}
<div className='mb-4 border-b pb-4'>
{renderInfoBlock(
'Budget total (M€)',
'Budget total (en millions d’€)',
budgetTotal1 ? formatNumberInteger(Math.round(budgetTotal1 / 1_000_000)) : '—',
budgetTotal2 ? formatNumberInteger(Math.round(budgetTotal2 / 1_000_000)) : '—',
'bg-brand-3',
Expand Down
6 changes: 3 additions & 3 deletions front/app/community/[siren]/components/CommunityDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export function CommunityDetails({ community, compare, left, budgetTotal }: Comm
<InfoBlock
label='Budget total'
value={budgetEnMillions !== null ? formatNumberInteger(budgetEnMillions) : '—'}
unit='M€'
unit='millions d’€'
bgColor={compare ? (left ? 'bg-brand-3' : 'bg-primary-light') : 'bg-lime-100'}
/>
<InfoBlock
Expand Down Expand Up @@ -64,9 +64,9 @@ function InfoBlock({
<div className={`rounded-none rounded-br-2xl rounded-tl-2xl p-3 text-primary ${bgColor}`}>
<div className='flex flex-row items-center justify-between gap-2 sm:flex-col sm:items-start sm:justify-start sm:gap-1'>
<p className='text-base font-medium'>
{label === 'Budget total' && unit === 'M€' ? (
{label === 'Budget total' ? (
<>
<span className='sm:hidden'>Budget total (M€)</span>
<span className='sm:hidden'>Budget total (en millions d’€)</span>
<span className='hidden sm:inline'>{label}</span>
</>
) : (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const descriptionText =

export function FicheHeader({ community, similarCommunityList }: FicheHeaderProps) {
const communityTitle = community.nom;
const communityType = community.type;
const communityType = community.formattedType;
const location = community.code_postal ? `${community.code_postal}` : '';
const departementName = community.nom_departement;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { CommunityType } from '#utils/types';

type ComparisonProps = {
siren: string;
communityType?: CommunityType;
communityType: CommunityType;
};

export default function Comparison({ siren, communityType }: ComparisonProps) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { CommunityType } from '#utils/types';

type ComparisonProps = {
siren: string;
communityType?: CommunityType;
communityType: CommunityType;
};

export default function Comparison({ siren, communityType }: ComparisonProps) {
Expand Down
5 changes: 2 additions & 3 deletions front/app/community/[siren]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { fetchCommunityBudgetTotal } from '#utils/fetchers/communities-accounts/
import { fetchCommunities } from '#utils/fetchers/communities/fetchCommunities-server';
import { fetchSimilarCommunityList } from '#utils/fetchers/communities/fetchSimilarCommunityList-server';
import { fetchMostRecentTransparencyScore } from '#utils/fetchers/communities/fetchTransparencyScore-server';
import type { CommunityType } from '#utils/types';
import { TransparencyScore } from '@/components/TransparencyScore/constants';

import { FicheHeader } from './components/FicheHeader/FicheHeader';
Expand Down Expand Up @@ -66,12 +65,12 @@ export default async function CommunityPage({ params }: CommunityPageProps) {
<TransparencyScoreWithTrend score={score} trend={trend} />
<FicheMarchesPublics
siren={siren}
communityType={community.type as CommunityType}
communityType={community.type}
communityName={community.nom}
/>
<FicheSubventions
siren={siren}
communityType={community.type as CommunityType}
communityType={community.type}
communityName={community.nom}
/>
</div>
Expand Down
3 changes: 2 additions & 1 deletion front/app/models/community.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ export type Community = {
/** Primary key [char9] */
siren: string;
/** Primary key */
type: CommunityType | string;
type: CommunityType;
formattedType: string;
nom: string;
code_insee: string;
code_insee_departement: string;
Expand Down
4 changes: 2 additions & 2 deletions front/components/Communities/CommunityBasics.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { Community } from '#app/models/community';

export default function CommunityBasics({ community }: { community: Community }) {
const { nom, type, code_postal, nom_departement } = community;
const { nom, formattedType, code_postal, nom_departement } = community;
return (
<div>
<h3 className='text-h2'>{nom}</h3>
<h4 className='text-h4 mt-4'>
{type} {nom_departement && <>· {nom_departement}</>} {code_postal && <>· {code_postal}</>}
{formattedType} {nom_departement && <>· {nom_departement}</>} {code_postal && <>· {code_postal}</>}
</h4>
</div>
);
Expand Down
41 changes: 26 additions & 15 deletions front/components/DataViz/ComparisonContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ import {
DropdownMenuItem,
DropdownMenuTrigger,
} from '#components/ui/dropdown-menu';
import { type Scope, useComparisonScope } from '#hooks/useTabState';
import { downloadSVGChart } from '#utils/downloader/downloadSVGChart';
import { getDefaultComparisonScope } from '#utils/helpers/getDefaultComparisonScope';
import { hasRealComparisonData } from '#utils/placeholderDataGenerators';
import type { CommunityType } from '#utils/types';
import { CommunityType, ScopeType } from '#utils/types';
import { getMonetaryUnit } from '#utils/utils';
import { useQuery } from '@tanstack/react-query';
import { ChevronDown } from 'lucide-react';
Expand All @@ -24,17 +23,16 @@ import { TabHeader } from '../../app/community/[siren]/components/TabHeader';
import LoadingOverlay from '../ui/LoadingOverlay';
import ComparisonChart, { type ComparisonData, type ComparisonTheme } from './ComparisonChart';
import MobileComparisonChart from './MobileComparisonChart';
import { formatScopeType } from '#utils/format';

type ComparisonContainerProps = {
siren: string;
communityType?: CommunityType;
communityType: CommunityType;
apiEndpoint: string;
theme: ComparisonTheme;
dataType: 'marches-publics' | 'subventions';
};

const SCOPES = ['Départemental', 'Régional', 'National'] as const;

// Generic fetch function
const fetchComparisonData = async (endpoint: string, scope: string): Promise<ComparisonData[]> => {
const response = await fetch(`${endpoint}?scope=${scope.toLowerCase()}`);
Expand All @@ -46,14 +44,26 @@ const fetchComparisonData = async (endpoint: string, scope: string): Promise<Com
return response.json();
};

const getAvailableScopes = (communtyType: CommunityType): ScopeType[] => {
if(communtyType === CommunityType.Region) {
return [ScopeType.Nation];
}else if(communtyType === CommunityType.Departement) {
return [ScopeType.Nation, ScopeType.Region];
}else{
return [ScopeType.Nation, ScopeType.Region, ScopeType.Departement];
}
};

// Extracted components for better composition
const ScopeDropdown = ({
selectedScope,
onScopeChange,
disabled,
communityType,
disabled
}: {
selectedScope: Scope;
onScopeChange: (scope: Scope) => void;
selectedScope: ScopeType;
onScopeChange: (scope: ScopeType) => void;
communityType: CommunityType;
disabled?: boolean;
}) => (
<DropdownMenu>
Expand All @@ -63,14 +73,14 @@ const ScopeDropdown = ({
className='h-12 gap-2 rounded-bl-none rounded-br-lg rounded-tl-lg rounded-tr-none border-gray-300 bg-white px-4 hover:bg-gray-50'
disabled={disabled}
>
{selectedScope}
{formatScopeType(selectedScope)}
<ChevronDown className='h-4 w-4' />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
{SCOPES.map((scope) => (
{getAvailableScopes(communityType).map((scope) => (
<DropdownMenuItem key={scope} onClick={() => onScopeChange(scope)}>
{scope}
{formatScopeType(scope)}
</DropdownMenuItem>
))}
</DropdownMenuContent>
Expand Down Expand Up @@ -221,7 +231,7 @@ const ChartWithLegend = ({
<span className='text-sm'>{data[0]?.regionalLabel || 'Moyenne régionale'}</span>
</div>
</div>
<div className='text-xs font-medium text-primary'>Montants exprimés en {unit}</div>
<div className='text-xs font-medium text-primary'>Montants exprimés {unit}</div>
</div>
</div>
</div>
Expand All @@ -235,8 +245,8 @@ export default function ComparisonContainer({
theme,
dataType,
}: ComparisonContainerProps) {
const defaultScope = communityType ? getDefaultComparisonScope(communityType) : 'Départemental';
const [selectedScope, setSelectedScope] = useComparisonScope(defaultScope);
const defaultScope = communityType ? getDefaultComparisonScope(communityType) : ScopeType.Departement;
const [selectedScope, setSelectedScope] = useState(defaultScope);
const [isMobile, setIsMobile] = useState(false);
const chartContainerRef = useRef<HTMLDivElement>(null);

Expand Down Expand Up @@ -338,7 +348,7 @@ export default function ComparisonContainer({
);
};

const handleScopeChange = (scope: Scope) => {
const handleScopeChange = (scope: ScopeType) => {
if (scope !== selectedScope) {
setSelectedScope(scope);
}
Expand All @@ -359,6 +369,7 @@ export default function ComparisonContainer({
<ScopeDropdown
selectedScope={selectedScope}
onScopeChange={handleScopeChange}
communityType={communityType}
disabled={isFetching}
/>
<DownloadSelector
Expand Down
4 changes: 2 additions & 2 deletions front/components/DataViz/EvolutionContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ type EvolutionContainerProps = {

// Title mapping
const TITLES = {
'marches-publics': 'Évolution des marchés publics au cours du temps',
subventions: 'Évolution des subventions au cours du temps',
'marches-publics': 'Évolution des marchés publics',
subventions: 'Évolution des subventions',
};

// Switch labels mapping
Expand Down
5 changes: 4 additions & 1 deletion front/components/DataViz/Treemap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -393,9 +393,12 @@ function Treemap({
className='pointer-events-none'
>
<div
className='pointer-events-none truncate text-sm font-medium leading-tight'
className='pointer-events-none w-full h-full overflow-hidden text-sm font-medium leading-tight'
style={{
color: getTextColor(colorMap[leaf.data.name], colorPalette),
maxHeight: `${leaf.y1 - leaf.y0 - 46}px`,
WebkitMaskImage: "linear-gradient(180deg, black 60%, transparent)",
maskImage: "linear-gradient(180deg, black 60%, transparent)",
}}
>
{formatFirstLetterToUppercase(leaf.data.name)}
Expand Down
38 changes: 4 additions & 34 deletions front/hooks/useTabState.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use client';

import { ScopeType } from '#utils/types';
import { parseAsString, useQueryState } from 'nuqs';

export const TAB_VALUES = {
Expand All @@ -25,40 +26,9 @@ export function useSubventionsTab(defaultValue: string = TAB_VALUES.SUBVENTIONS.
return useQueryState('sub', parseAsString.withDefault(defaultValue));
}

// Scope values for comparison dropdowns
export const SCOPE_VALUES = {
DEPARTEMENTAL: 'departemental',
REGIONAL: 'regional',
NATIONAL: 'national',
} as const;

export type Scope = 'Départemental' | 'Régional' | 'National';

// Map display values to URL values
const SCOPE_URL_MAP: Record<Scope, string> = {
Départemental: SCOPE_VALUES.DEPARTEMENTAL,
Régional: SCOPE_VALUES.REGIONAL,
National: SCOPE_VALUES.NATIONAL,
};

// Map URL values to display values
const URL_SCOPE_MAP: Record<string, Scope> = {
[SCOPE_VALUES.DEPARTEMENTAL]: 'Départemental',
[SCOPE_VALUES.REGIONAL]: 'Régional',
[SCOPE_VALUES.NATIONAL]: 'National',
};

export function useComparisonScope(defaultValue: Scope = 'Départemental') {
const [urlScope, setUrlScope] = useQueryState(
export function useComparisonScope(defaultValue: ScopeType = ScopeType.Departement) {
return useQueryState(
'scope',
parseAsString.withDefault(SCOPE_URL_MAP[defaultValue]),
parseAsString.withDefault(defaultValue),
);

const displayScope = URL_SCOPE_MAP[urlScope] || defaultValue;

const setScope = (scope: Scope) => {
setUrlScope(SCOPE_URL_MAP[scope]);
};

return [displayScope, setScope] as const;
}
4 changes: 2 additions & 2 deletions front/utils/fetchers/communities/fetchCommunities-server.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Community } from '#app/models/community';
import { getQueryFromPool } from '#utils/db';
import { formatCommunityType, formatDepartmentName, formatLocationName } from '#utils/format';
import { CommunityType } from '#utils/types.js';
import { CommunityType } from '#utils/types';

import type { Pagination } from '../types';
import { type CommunitiesOptions, createSQLQueryParams } from './createSQLQueryParams';
Expand All @@ -23,7 +23,7 @@ export async function fetchCommunities(
return communities.map((community) => ({
...community,
nom: formatLocationName(community.nom),
type: formatCommunityType(community.type as CommunityType),
formattedType: formatCommunityType(community.type as CommunityType),
nom_departement: community.nom_departement
? formatDepartmentName(community.nom_departement)
: null,
Expand Down
Loading