mirror of
https://github.com/kjanat/livedash-node.git
synced 2026-01-16 06:32:10 +01:00
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:
@ -384,7 +384,7 @@ function DashboardContent() {
|
||||
</div>
|
||||
{/* Word Cloud and World Map */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<div className="bg-white p-6 rounded-xl shadow">
|
||||
<div className="bg-white p-6 rounded-xl shadow overflow-hidden">
|
||||
<h3 className="font-bold text-lg text-gray-800 mb-4">
|
||||
Categories Word Cloud
|
||||
</h3>
|
||||
|
||||
@ -39,15 +39,15 @@ export default function UserManagement({ session }: UserManagementProps) {
|
||||
return (
|
||||
<div className="bg-white p-6 rounded-xl shadow mb-6">
|
||||
<h2 className="font-bold text-lg mb-4">User Management</h2>
|
||||
<div className="flex gap-2 mb-3">
|
||||
<div className="flex flex-col sm:flex-row gap-2 mb-3">
|
||||
<input
|
||||
className="border px-3 py-2 rounded"
|
||||
className="border px-3 py-2 rounded w-full sm:w-auto"
|
||||
placeholder="Email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
/>
|
||||
<select
|
||||
className="border px-3 py-2 rounded"
|
||||
className="border px-3 py-2 rounded w-full sm:w-auto"
|
||||
value={role}
|
||||
onChange={(e) => setRole(e.target.value)}
|
||||
>
|
||||
@ -56,7 +56,7 @@ export default function UserManagement({ session }: UserManagementProps) {
|
||||
<option value="auditor">Auditor</option>
|
||||
</select>
|
||||
<button
|
||||
className="bg-blue-600 text-white rounded px-4"
|
||||
className="bg-blue-600 text-white rounded px-4 py-2 sm:py-0 w-full sm:w-auto"
|
||||
onClick={inviteUser}
|
||||
>
|
||||
Invite
|
||||
|
||||
@ -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}`;
|
||||
|
||||
@ -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();
|
||||
},
|
||||
},
|
||||
|
||||
@ -99,14 +99,13 @@ export default function GeographicMap({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="h-full w-full" style={{ height }}>
|
||||
{Object.keys(countries).length > 0 ? (
|
||||
<div style={{ height: `${height}px`, width: "100%" }} className="relative">
|
||||
<Map countryData={countryData} maxCount={maxCount} />
|
||||
) : (
|
||||
<div className="h-full w-full bg-gray-100 flex items-center justify-center">
|
||||
No geographic data available
|
||||
</div>
|
||||
)}
|
||||
<style jsx global>{`
|
||||
.leaflet-control-attribution {
|
||||
display: none !important;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
// Allow cross-origin requests from specific origins in development
|
||||
allowedDevOrigins: ["192.168.1.2","localhost","propc"],
|
||||
allowedDevOrigins: ["192.168.1.2", "localhost", "propc"],
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
|
||||
Reference in New Issue
Block a user