Compare commits

...

2 Commits

Author SHA1 Message Date
MehrdadAdabi
5d550217db Merge branch 'main' of http://git.sepehrdata.com/Saeed0920/inogen 2025-10-14 16:39:57 +03:30
MehrdadAdabi
b1db9e8685 fix: change date-picker logic 2025-10-14 16:39:29 +03:30
15 changed files with 194 additions and 132 deletions

View File

@ -1,4 +1,3 @@
import jalaali from "jalaali-js";
import { Book, CheckCircle } from "lucide-react";
import { useEffect, useState } from "react";
import toast from "react-hot-toast";
@ -16,6 +15,7 @@ import { ChartContainer } from "~/components/ui/chart";
import { MetricCard } from "~/components/ui/metric-card";
import { Progress } from "~/components/ui/progress";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs";
import { useStoredDate } from "~/hooks/useStoredDate";
import apiService from "~/lib/api";
import { EventBus, formatNumber } from "~/lib/utils";
import type { CalendarDate } from "~/types/util.type";
@ -25,7 +25,6 @@ import { InteractiveBarChart } from "./interactive-bar-chart";
import { DashboardLayout } from "./layout";
export function DashboardHome() {
const { jy } = jalaali.toJalaali(new Date());
const [dashboardData, setDashboardData] = useState<any | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
@ -42,10 +41,7 @@ export function DashboardHome() {
}[]
>([]);
const [date, setDate] = useState<CalendarDate>({
start: `${jy}/01/01`,
end: `${jy}/12/30`,
});
const [date, setDate] = useStoredDate();
useEffect(() => {
EventBus.on("dateSelected", (date: CalendarDate) => {
@ -54,7 +50,7 @@ export function DashboardHome() {
}, []);
useEffect(() => {
fetchDashboardData();
if (date?.end && date?.start) fetchDashboardData();
}, [date]);
const fetchDashboardData = async () => {

View File

@ -65,7 +65,7 @@ const monthList: Array<MonthItem> = [
id: "month-4",
label: "زمستان",
start: "10/01",
end: "12/29",
end: "12/30",
},
];
@ -87,12 +87,34 @@ export function Header({
until: jy,
});
const [selectedDate, setSelectedDate] = useState<CurrentDay>({
const [selectedDate, setSelectedDate] = useState<CurrentDay>({});
useEffect(() => {
const storedDate = localStorage.getItem("dateSelected");
if (storedDate) {
const parsedDate = JSON.parse(storedDate);
setSelectedDate(parsedDate);
const sinceYear = parsedDate.start
? parseInt(parsedDate.start.split("/")[0], 10)
: jy;
const untilYear = parsedDate.end
? parseInt(parsedDate.end.split("/")[0], 10)
: jy;
setCurrentYear({ since: sinceYear, until: untilYear });
} else {
const defaultDate = {
sinceMonth: "بهار",
fromMonth: "زمستان",
start: `${currentYear.since}/01/01`,
end: `${currentYear.until}/12/30`,
});
start: `${jy}/01/01`,
end: `${jy}/12/30`,
};
setSelectedDate(defaultDate);
localStorage.setItem("dateSelected", JSON.stringify(defaultDate));
setCurrentYear({ since: jy, until: jy });
}
}, []);
const redirectHandler = async () => {
try {
@ -119,6 +141,7 @@ export function Header({
start: `${newSince}/${selectedDate.start?.split("/").slice(1).join("/")}`,
};
setSelectedDate(updatedDate);
localStorage.setItem("dateSelected", JSON.stringify(updatedDate));
EventBus.emit("dateSelected", updatedDate);
};
@ -132,6 +155,7 @@ export function Header({
sinceMonth: val.label,
};
setSelectedDate(data);
localStorage.setItem("dateSelected", JSON.stringify(data));
EventBus.emit("dateSelected", data);
};
@ -150,6 +174,7 @@ export function Header({
end: `${newUntil}/${selectedDate.end?.split("/").slice(1).join("/")}`,
};
setSelectedDate(updatedDate);
localStorage.setItem("dateSelected", JSON.stringify(updatedDate));
EventBus.emit("dateSelected", updatedDate);
};
@ -163,6 +188,7 @@ export function Header({
fromMonth: val.label,
};
setSelectedDate(data);
localStorage.setItem("dateSelected", JSON.stringify(data));
EventBus.emit("dateSelected", data);
toggleCalendar();
};

View File

@ -1,4 +1,3 @@
import jalaali from "jalaali-js";
import {
BrainCircuit,
ChevronDown,
@ -34,6 +33,7 @@ import {
TableHeader,
TableRow,
} from "~/components/ui/table";
import { useStoredDate } from "~/hooks/useStoredDate";
import apiService from "~/lib/api";
import { EventBus, formatCurrency, formatNumber } from "~/lib/utils";
import type { CalendarDate } from "~/types/util.type";
@ -148,18 +148,13 @@ const columns = [
];
export function DigitalInnovationPage() {
const { jy } = jalaali.toJalaali(new Date());
const [projects, setProjects] = useState<DigitalInnovationMetrics[]>([]);
const [loading, setLoading] = useState(false);
const [loadingMore, setLoadingMore] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const [pageSize] = useState(20);
const [hasMore, setHasMore] = useState(true);
// const [totalCount, setTotalCount] = useState(0);
const [date, setDate] = useState<CalendarDate>({
start: `${jy}/01/01`,
end: `${jy}/12/30`,
});
const [date, setDate] = useStoredDate();
const [actualTotalCount, setActualTotalCount] = useState(0);
const [statsLoading, setStatsLoading] = useState(false);
const [rating, setRating] = useState<ListItem[]>([]);
@ -363,10 +358,25 @@ export function DigitalInnovationPage() {
}
}, [hasMore, loading, loadingMore]);
// useEffect(() => {
// const storedDate = localStorage.getItem("dateSelected");
// if (storedDate) {
// setDate(JSON.parse(storedDate));
// } else {
// setDate({
// start: `${jy}/01/01`,
// end: `${jy}/12/30`,
// });
// }
// }, []);
useEffect(() => {
if (date?.start && date?.end) {
fetchTable(true);
fetchTotalCount();
fetchStats();
}
}, [sortConfig, date]);
useEffect(() => {
@ -378,7 +388,7 @@ export function DigitalInnovationPage() {
}, []);
useEffect(() => {
if (currentPage > 1) {
if (currentPage > 1 && date?.start && date?.end) {
fetchTable(false);
}
}, [currentPage]);

View File

@ -1,4 +1,3 @@
// import moment from "moment-jalaali";
import { useCallback, useEffect, useRef, useState } from "react";
import {
Bar,
@ -28,7 +27,6 @@ import {
} from "~/components/ui/table";
import { EventBus, formatNumber } from "~/lib/utils";
import jalaali from "jalaali-js";
import {
Building2,
ChevronDown,
@ -44,13 +42,16 @@ import {
UsersIcon,
Zap,
} from "lucide-react";
import moment from "moment-jalaali";
import toast from "react-hot-toast";
import { useStoredDate } from "~/hooks/useStoredDate";
import apiService from "~/lib/api";
import { formatCurrency } from "~/lib/utils";
import type { CalendarDate } from "~/types/util.type";
import DashboardLayout from "../layout";
// moment.loadPersian({ usePersianDigits: true });
moment.loadPersian({ usePersianDigits: true });
interface GreenInnovationData {
WorkflowID: string;
approved_budget: string;
@ -159,7 +160,6 @@ const columns = [
];
export function GreenInnovationPage() {
const { jy } = jalaali.toJalaali(new Date());
const [projects, setProjects] = useState<GreenInnovationData[]>([]);
const [loading, setLoading] = useState(false);
const [loadingMore, setLoadingMore] = useState(false);
@ -169,10 +169,8 @@ export function GreenInnovationPage() {
const [totalCount, setTotalCount] = useState(0);
const [actualTotalCount, setActualTotalCount] = useState(0);
const [statsLoading, setStatsLoading] = useState(false);
const [date, setDate] = useState<CalendarDate>({
start: `${jy}/01/01`,
end: `${jy}/12/30`,
});
const [date, setDate] = useStoredDate();
const [stats, setStats] = useState<stateCounter>();
const [sortConfig, setSortConfig] = useState<SortConfig>({
field: "start_date",
@ -376,12 +374,14 @@ export function GreenInnovationPage() {
}, [hasMore, loading]);
useEffect(() => {
if (date.end && date.start) {
fetchProjects(true);
fetchTotalCount();
}
}, [sortConfig, date]);
useEffect(() => {
fetchStats();
if (date.end && date.start) fetchStats();
}, [selectedProjects, date]);
useEffect(() => {

View File

@ -19,7 +19,6 @@ import {
TableRow,
} from "~/components/ui/table";
import jalaali from "jalaali-js";
import {
ChevronDown,
ChevronUp,
@ -40,6 +39,7 @@ import {
ResponsiveContainer,
XAxis,
} from "recharts";
import { useStoredDate } from "~/hooks/useStoredDate";
import apiService from "~/lib/api";
import { EventBus, formatCurrency, formatNumber } from "~/lib/utils";
import type { CalendarDate } from "~/types/util.type";
@ -179,7 +179,6 @@ const dialogChartData = [
];
export function InnovationBuiltInsidePage() {
const { jy } = jalaali.toJalaali(new Date());
const [projects, setProjects] = useState<innovationBuiltInDate[]>([]);
const [loading, setLoading] = useState(false);
const [loadingMore, setLoadingMore] = useState(false);
@ -194,10 +193,8 @@ export function InnovationBuiltInsidePage() {
field: "start_date",
direction: "asc",
});
const [date, setDate] = useState<CalendarDate>({
start: `${jy}/01/01`,
end: `${jy}/12/30`,
});
const [date, setDate] = useStoredDate();
const [tblAvarage, setTblAvarage] = useState<number>(0);
const [selectedProjects, setSelectedProjects] =
useState<Set<string | number>>();
@ -436,11 +433,11 @@ export function InnovationBuiltInsidePage() {
}, []);
useEffect(() => {
fetchProjects(true);
if (date.start && date.end) fetchProjects(true);
}, [sortConfig, date]);
useEffect(() => {
fetchStats();
if (date.end && date.start) fetchStats();
}, [selectedProjects, date]);
useEffect(() => {
@ -724,7 +721,7 @@ export function InnovationBuiltInsidePage() {
return (
<DashboardLayout title="نوآوری ساخت داخل">
<div className="space-y-4 justify-between gap-8 grid pl-3.5 sm:grid-cols-1 xl:grid-cols-[35%_65%]">
<div className="space-y-4 justify-between gap-8 grid pl-6 sm:grid-cols-1 xl:grid-cols-[35%_65%]">
{/* Stats Cards */}
<div className="flex w-full mb-0">
<div className="flex flex-col w-full justify-between gap-2">

View File

@ -1,4 +1,3 @@
import jalaali from "jalaali-js";
import {
ChevronDown,
ChevronUp,
@ -43,6 +42,7 @@ import {
TableHeader,
TableRow,
} from "~/components/ui/table";
import { useStoredDate } from "~/hooks/useStoredDate";
import apiService from "~/lib/api";
import { EventBus, formatCurrency, formatNumber } from "~/lib/utils";
import type { CalendarDate } from "~/types/util.type";
@ -261,7 +261,6 @@ const VerticalBarChart = memo<{
const MemoizedVerticalBarChart = VerticalBarChart;
export function ManageIdeasTechPage() {
const { jy } = jalaali.toJalaali(new Date());
const [ideas, setIdeas] = useState<IdeaData[]>([]);
const [loading, setLoading] = useState(false);
const [loadingMore, setLoadingMore] = useState(false);
@ -276,10 +275,7 @@ export function ManageIdeasTechPage() {
field: "idea_title",
direction: "asc",
});
const [date, setDate] = useState<CalendarDate>({
start: `${jy}/01/01`,
end: `${jy}/12/30`,
});
const [date, setDate] = useStoredDate();
// People ranking state
const [peopleRanking, setPeopleRanking] = useState<PersonRanking[]>([]);
@ -417,11 +413,13 @@ export function ManageIdeasTechPage() {
}, []);
useEffect(() => {
if (date.end && date.start) {
fetchIdeas(true);
fetchTotalCount();
fetchPeopleRanking();
fetchChartData();
fetchStatsData();
}
}, [sortConfig, date]);
useEffect(() => {

View File

@ -1,4 +1,3 @@
import jalaali from "jalaali-js";
import {
Building2,
ChevronDown,
@ -35,6 +34,7 @@ import {
TableHeader,
TableRow,
} from "~/components/ui/table";
import { useStoredDate } from "~/hooks/useStoredDate";
import apiService from "~/lib/api";
import { EventBus, formatNumber } from "~/lib/utils";
import type { CalendarDate } from "~/types/util.type";
@ -119,18 +119,13 @@ const columns = [
];
export function ProcessInnovationPage() {
const { jy } = jalaali.toJalaali(new Date());
const [projects, setProjects] = useState<ProcessInnovationData[]>([]);
const [loading, setLoading] = useState(false);
const [loadingMore, setLoadingMore] = useState(false);
const [currentPage, setCurrentPage] = useState(1);
const [pageSize] = useState(20);
const [hasMore, setHasMore] = useState(true);
// const [totalCount, setTotalCount] = useState(0);
const [date, setDate] = useState<CalendarDate>({
start: `${jy}/01/01`,
end: `${jy}/12/30`,
});
const [date, setDate] = useStoredDate();
const [actualTotalCount, setActualTotalCount] = useState(0);
const [statsLoading, setStatsLoading] = useState(false);
const [stats, setStats] = useState<InnovationStats>({
@ -345,12 +340,14 @@ export function ProcessInnovationPage() {
}, []);
useEffect(() => {
if (date?.start && date?.end) {
fetchProjects(true);
fetchTotalCount();
}
}, [sortConfig, date]);
useEffect(() => {
fetchStats();
if (date?.start && date?.end) fetchStats();
}, [selectedProjects, date]);
useEffect(() => {

View File

@ -14,7 +14,6 @@ import {
PopoverTrigger,
} from "~/components/ui/popover";
import jalaali from "jalaali-js";
import {
CartesianGrid,
Legend,
@ -42,6 +41,7 @@ import {
TableRow,
} from "~/components/ui/table";
import { Tooltip as TooltipSh, TooltipTrigger } from "~/components/ui/tooltip";
import { useStoredDate } from "~/hooks/useStoredDate";
import apiService from "~/lib/api";
import { EventBus, formatNumber, handleDataValue } from "~/lib/utils";
import type { CalendarDate } from "~/types/util.type";
@ -199,7 +199,6 @@ export default function Timeline(valueTimeLine: string) {
export function ProductInnovationPage() {
// const [showPopup, setShowPopup] = useState(false);
const { jy } = jalaali.toJalaali(new Date());
const [projects, setProjects] = useState<ProductInnovationData[]>([]);
const [loading, setLoading] = useState(false);
const [loadingMore, setLoadingMore] = useState(false);
@ -264,10 +263,8 @@ export function ProductInnovationPage() {
},
});
const [date, setDate] = useState<CalendarDate>({
start: `${jy}/01/01`,
end: `${jy}/12/30`,
});
const [date, setDate] = useStoredDate();
const observerRef = useRef<HTMLDivElement>(null);
const fetchingRef = useRef(false);
@ -520,11 +517,11 @@ export function ProductInnovationPage() {
}, []);
useEffect(() => {
fetchProjects(true);
if (date.end && date.start) fetchProjects(true);
}, [sortConfig, date]);
useEffect(() => {
fetchStats();
if (date.end && date.start) fetchStats();
}, [date]);
useEffect(() => {

View File

@ -1,4 +1,3 @@
import jalaali from "jalaali-js";
import { ChevronDown, ChevronUp, RefreshCw } from "lucide-react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import toast from "react-hot-toast";
@ -13,6 +12,7 @@ import {
TableHeader,
TableRow,
} from "~/components/ui/table";
import { useStoredDate } from "~/hooks/useStoredDate";
import apiService from "~/lib/api";
import { EventBus, formatCurrency, formatNumber } from "~/lib/utils";
import type { CalendarDate } from "~/types/util.type";
@ -154,7 +154,6 @@ const columns: ColumnDef[] = [
];
export function ProjectManagementPage() {
const { jy } = jalaali.toJalaali(new Date());
const [projects, setProjects] = useState<ProjectData[]>([]);
const [loading, setLoading] = useState(false);
const [loadingMore, setLoadingMore] = useState(false);
@ -171,10 +170,12 @@ export function ProjectManagementPage() {
const fetchingRef = useRef(false);
const scrollTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const scrollContainerRef = useRef<HTMLDivElement>(null);
const [date, setDate] = useState<CalendarDate>({
start: `${jy}/01/01`,
end: `${jy}/12/30`,
});
// const [date, setDate] = useState<CalendarDate>({
// start: `${jy}/01/01`,
// end: `${jy}/12/30`,
// });
const [date, setDate] = useStoredDate();
const fetchProjects = async (reset = false) => {
// Prevent concurrent API calls
@ -288,8 +289,10 @@ export function ProjectManagementPage() {
}, [hasMore, loading, loadingMore]);
useEffect(() => {
if (date.end && date.start) {
fetchProjects(true);
fetchTotalCount();
}
}, [sortConfig, date]);
useEffect(() => {

View File

@ -1,4 +1,3 @@
import jalaali from "jalaali-js";
import { useEffect, useReducer, useRef, useState } from "react";
import {
Bar,
@ -12,6 +11,7 @@ import {
} from "recharts";
import { Dialog, DialogContent, DialogHeader } from "~/components/ui/dialog";
import { Skeleton } from "~/components/ui/skeleton";
import { useStoredDate } from "~/hooks/useStoredDate";
import apiService from "~/lib/api";
import { EventBus, formatNumber } from "~/lib/utils";
import type { CalendarDate } from "~/types/util.type";
@ -118,7 +118,6 @@ export function StrategicAlignmentPopup({
open,
onOpenChange,
}: StrategicAlignmentPopupProps) {
const { jy } = jalaali.toJalaali(new Date());
const [data, setData] = useState<StrategicAlignmentData[]>([]);
const [loading, setLoading] = useState(false);
const contentRef = useRef<HTMLDivElement | null>(null);
@ -128,10 +127,8 @@ export function StrategicAlignmentPopup({
dropDownItems: [],
});
const [date, setDate] = useState<CalendarDate>({
start: `${jy}/01/01`,
end: `${jy}/12/30`,
});
const [date, setDate] = useStoredDate();
useEffect(() => {
if (open) {
fetchData();

View File

@ -12,6 +12,7 @@ import {
} from "recharts";
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
import { CustomBarChart } from "~/components/ui/custom-bar-chart";
import { useStoredDate } from "~/hooks/useStoredDate";
import apiService from "~/lib/api";
import { EventBus, formatNumber } from "~/lib/utils";
import type { CalendarDate } from "~/types/util.type";
@ -63,7 +64,9 @@ export function InfoPanel({ selectedCompany }: InfoPanelProps) {
const [counts, setCounts] = useState<EcosystemCounts | null>(null);
const [processData, setProcessData] = useState<ProcessActorsData[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [date, setDate] = useState<CalendarDate>();
// const [date, setDate] = useState<CalendarDate>();
const [date, setDate] = useStoredDate();
useEffect(() => {
EventBus.on("dateSelected", (date: CalendarDate) => {
if (date) {
@ -73,7 +76,7 @@ export function InfoPanel({ selectedCompany }: InfoPanelProps) {
}, []);
useEffect(() => {
fetchCounts();
if (date.end && date.start) fetchCounts();
}, [date]);
const fetchCounts = async () => {

View File

@ -1,5 +1,6 @@
import * as d3 from "d3";
import { useCallback, useEffect, useRef, useState } from "react";
import { useStoredDate } from "~/hooks/useStoredDate";
import { EventBus } from "~/lib/utils";
import type { CalendarDate } from "~/types/util.type";
import { useAuth } from "../../contexts/auth-context";
@ -73,7 +74,9 @@ export function NetworkGraph({
const [error, setError] = useState<string | null>(null);
const { token } = useAuth();
const [date, setDate] = useState<CalendarDate>();
// const [date, setDate] = useState<CalendarDate>();
const [date, setDate] = useStoredDate();
useEffect(() => {
EventBus.on("dateSelected", (date: CalendarDate) => {
if (date) {
@ -120,7 +123,10 @@ export function NetworkGraph({
setIsLoading(true);
try {
const res = await apiService.call<any[]>({
graph_production_function: {},
graph_production_function: {
start_date: date.start || null,
end_date: date.end || null,
},
});
if (aborted) return;
@ -484,6 +490,7 @@ export function NetworkGraph({
onLoadingChange?.(true);
try {
if (date.start && date.end) {
const res = await callAPI(d.stageid);
const responseData = JSON.parse(res.data);
const fieldValues =
@ -513,6 +520,7 @@ export function NetworkGraph({
};
onNodeClick(companyDetails);
}
} catch (error) {
console.error("Failed to fetch company details:", error);
// Keep the basic details already shown
@ -541,7 +549,7 @@ export function NetworkGraph({
return () => {
simulation.stop();
};
}, [nodes, links, isLoading, isMounted, onNodeClick, callAPI]);
}, [nodes, links, isLoading, isMounted, onNodeClick, callAPI, date]);
if (error) {
return (

View File

@ -0,0 +1,27 @@
import jalaali from "jalaali-js";
import { useEffect, useState } from "react";
import type { CalendarDate } from "~/types/util.type";
const { jy } = jalaali.toJalaali(new Date());
export function useStoredDate(): [
CalendarDate,
React.Dispatch<React.SetStateAction<CalendarDate>>,
] {
const [date, setDate] = useState<CalendarDate>({});
useEffect(() => {
const storedDate = localStorage.getItem("dateSelected");
if (storedDate) {
setDate(JSON.parse(storedDate));
} else {
setDate({
start: `${jy}/01/01`,
end: `${jy}/12/30`,
});
}
}, [jy]);
return [date, setDate];
}

View File

@ -1,28 +1,26 @@
import type { Route } from "./+types/ecosystem";
import moment from "moment-jalaali";
import React from "react";
import { ProtectedRoute } from "~/components/auth/protected-route";
import { DashboardLayout } from "~/components/dashboard/layout";
import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card";
import { InfoPanel } from "~/components/ecosystem/info-panel";
import { NetworkGraph } from "~/components/ecosystem/network-graph";
import { Card, CardContent } from "~/components/ui/card";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "~/components/ui/dialog";
import { NetworkGraph } from "~/components/ecosystem/network-graph";
import { InfoPanel } from "~/components/ecosystem/info-panel";
import { useAuth } from "~/contexts/auth-context";
import moment from "moment-jalaali";
import type { Route } from "./+types/ecosystem";
// Get API base URL at module level to avoid process.env access in browser
const API_BASE_URL =
import.meta.env.VITE_API_URL || "https://inogen-back.pelekan.org/api";
// Import the CompanyDetails type
import type { CompanyDetails } from "~/components/ecosystem/network-graph";
import { formatNumber } from "~/lib/utils";
import { Hexagon } from "lucide-react";
import type { CompanyDetails } from "~/components/ecosystem/network-graph";
export function meta({}: Route.MetaArgs) {
return [
@ -89,7 +87,10 @@ export default function EcosystemPage() {
<div className="lg:col-span-8 h-full">
<Card className="h-full overflow-hidden bg-transparent border-[#3F415A]">
<CardContent className="p-0 h-full bg-transparent">
<NetworkGraph onNodeClick={handleNodeClick} onLoadingChange={handleLoadingChange} />
<NetworkGraph
onNodeClick={handleNodeClick}
onLoadingChange={handleLoadingChange}
/>
</CardContent>
</Card>
</div>
@ -225,7 +226,9 @@ export default function EcosystemPage() {
<span className="text-right min-w-1/3">
<span className="font-persian text-sm font-normal text-right">
{handleValue(field.V)}
{field.U && <span className="mr-1">({field.U})</span>}
{field.U && (
<span className="mr-1">({field.U})</span>
)}
</span>
</span>
</div>

View File

@ -1,6 +1,6 @@
export interface CalendarDate {
start: string;
end: string;
start?: string;
end?: string;
sinceMonth?: string;
untilMonth?: string;
}