Improves dashboard UI and user management

Enhances the dashboard's visual presentation and user management
functionality.

- Addresses a layout issue by adding overflow hidden to the word cloud container.
- Improves user management form responsiveness on smaller screens.
- Enhances language name display in the pie chart using a dedicated function and ensures correct ISO code display.
- Refines donut chart's center text styling for better readability.
- Fixes geographic map's height and removes attribution for a cleaner look.
This commit is contained in:
2025-05-22 05:05:04 +02:00
parent 00f2f5c59b
commit ac7cafd7b2
7 changed files with 43 additions and 49 deletions

View File

@ -1,6 +1,7 @@
"use client";
import { useEffect, useRef } from "react";
import Chart from "chart.js/auto";
import { getLocalizedLanguageName } from "../lib/localization"; // Corrected import path
interface SessionsData {
[date: string]: number;
@ -173,25 +174,16 @@ export function LanguagePieChart({ languages }: LanguagePieChartProps) {
topLanguages.push(["Other", otherCount]);
}
// Use Intl.DisplayNames to get localized language names from ISO codes
const languageDisplayNames = new Intl.DisplayNames(["en"], {
type: "language",
});
// Store original ISO codes for tooltip
const isoCodes = topLanguages.map(([lang]) => lang);
const labels = topLanguages.map(([lang]) => {
// Check if this is a valid ISO 639-1 language code
if (lang && lang !== "Other" && /^[a-z]{2}$/.test(lang)) {
try {
return languageDisplayNames.of(lang);
} catch {
// Empty catch block - no need to name the error parameter
return lang; // Fallback to code if display name can't be resolved
}
if (lang === "Other") {
return "Other";
}
return lang; // Return original string for "Other" or invalid codes
// Use getLocalizedLanguageName for robust name resolution
// Pass "en" to maintain consistency with previous behavior if navigator.language is different
return getLocalizedLanguageName(lang, "en");
});
const data = topLanguages.map(([, count]) => count);
@ -231,15 +223,16 @@ export function LanguagePieChart({ languages }: LanguagePieChartProps) {
const label = context.label || "";
const value = context.formattedValue || "";
const index = context.dataIndex;
const isoCode = isoCodes[index];
const originalIsoCode = isoCodes[index]; // Get the original code
// Only show ISO code if it's not "Other" and it's a valid 2-letter code
// Only show ISO code if it's not "Other"
// and it's a valid 2-letter code (check lowercase version)
if (
isoCode &&
isoCode !== "Other" &&
/^[a-z]{2}$/.test(isoCode)
originalIsoCode &&
originalIsoCode !== "Other" &&
/^[a-z]{2}$/.test(originalIsoCode.toLowerCase())
) {
return `${label} (${isoCode.toUpperCase()}): ${value}`;
return `${label} (${originalIsoCode.toUpperCase()}): ${value}`;
}
return `${label}: ${value}`;

View File

@ -97,28 +97,30 @@ export default function DonutChart({ data, centerText }: DonutChartProps) {
const ctx = chart.ctx;
ctx.restore();
const centerX = width / 2;
const centerY = height / 2;
// Title text
if (centerText.title) {
ctx.font = "14px Arial";
ctx.fillStyle = "#6B7280"; // text-gray-500
ctx.textBaseline = "middle";
ctx.font = "1rem sans-serif"; // Consistent font
ctx.fillStyle = "#6B7280"; // Tailwind gray-500
ctx.textAlign = "center";
ctx.fillText(centerText.title, width / 2, height / 2 - 10);
ctx.textBaseline = "middle"; // Align vertically
ctx.fillText(centerText.title, centerX, centerY - 10); // Adjust Y offset
}
// Value text
if (centerText.value !== undefined) {
ctx.font = "bold 20px Arial";
ctx.fillStyle = "#111827"; // text-gray-900
ctx.textBaseline = "middle";
ctx.font = "bold 1.5rem sans-serif"; // Consistent font, larger
ctx.fillStyle = "#1F2937"; // Tailwind gray-800
ctx.textAlign = "center";
ctx.textBaseline = "middle"; // Align vertically
ctx.fillText(
String(centerText.value),
width / 2,
height / 2 + 10
);
centerText.value.toString(),
centerX,
centerY + 15
); // Adjust Y offset
}
ctx.save();
},
},

View File

@ -99,14 +99,13 @@ export default function GeographicMap({
}
return (
<div className="h-full w-full" style={{ height }}>
{Object.keys(countries).length > 0 ? (
<Map countryData={countryData} maxCount={maxCount} />
) : (
<div className="h-full w-full bg-gray-100 flex items-center justify-center">
No geographic data available
</div>
)}
<div style={{ height: `${height}px`, width: "100%" }} className="relative">
<Map countryData={countryData} maxCount={maxCount} />
<style jsx global>{`
.leaflet-control-attribution {
display: none !important;
}
`}</style>
</div>
);
}