Merge branch 'main' into feat/green-innovation
This commit is contained in:
commit
3a4659c01a
File diff suppressed because it is too large
Load Diff
|
|
@ -34,7 +34,6 @@ import {
|
||||||
import apiService from "~/lib/api";
|
import apiService from "~/lib/api";
|
||||||
import toast from "react-hot-toast";
|
import toast from "react-hot-toast";
|
||||||
import { Funnel, Wrench, CirclePause, DollarSign } from "lucide-react";
|
import { Funnel, Wrench, CirclePause, DollarSign } from "lucide-react";
|
||||||
import ProjectDetail from "../projects/project-detail";
|
|
||||||
|
|
||||||
moment.loadPersian({ usePersianDigits: true });
|
moment.loadPersian({ usePersianDigits: true });
|
||||||
interface ProcessInnovationData {
|
interface ProcessInnovationData {
|
||||||
|
|
@ -150,7 +149,6 @@ export function ProcessInnovationPage() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleProjectDetails = (project: ProcessInnovationData) => {
|
const handleProjectDetails = (project: ProcessInnovationData) => {
|
||||||
console.log(project);
|
|
||||||
setSelectedProjectDetails(project);
|
setSelectedProjectDetails(project);
|
||||||
setDetailsDialogOpen(true);
|
setDetailsDialogOpen(true);
|
||||||
};
|
};
|
||||||
|
|
@ -169,7 +167,7 @@ export function ProcessInnovationPage() {
|
||||||
title: "جلوگیری از توقفات تولید",
|
title: "جلوگیری از توقفات تولید",
|
||||||
value: formatNumber(
|
value: formatNumber(
|
||||||
stats.productionStopsPreventionSum.toFixed?.(1) ??
|
stats.productionStopsPreventionSum.toFixed?.(1) ??
|
||||||
stats.productionStopsPreventionSum,
|
stats.productionStopsPreventionSum,
|
||||||
),
|
),
|
||||||
description: "تن افزایش یافته",
|
description: "تن افزایش یافته",
|
||||||
icon: <CirclePause />,
|
icon: <CirclePause />,
|
||||||
|
|
@ -199,7 +197,7 @@ export function ProcessInnovationPage() {
|
||||||
title: "کاهش خرابی های پرتکرار",
|
title: "کاهش خرابی های پرتکرار",
|
||||||
value: formatNumber(
|
value: formatNumber(
|
||||||
stats.frequentFailuresReductionSum.toFixed?.(1) ??
|
stats.frequentFailuresReductionSum.toFixed?.(1) ??
|
||||||
stats.frequentFailuresReductionSum,
|
stats.frequentFailuresReductionSum,
|
||||||
),
|
),
|
||||||
description: "مجموع درصد کاهش خرابی",
|
description: "مجموع درصد کاهش خرابی",
|
||||||
icon: <Wrench />,
|
icon: <Wrench />,
|
||||||
|
|
@ -402,7 +400,7 @@ export function ProcessInnovationPage() {
|
||||||
if (typeof payload === "string") {
|
if (typeof payload === "string") {
|
||||||
try {
|
try {
|
||||||
payload = JSON.parse(payload);
|
payload = JSON.parse(payload);
|
||||||
} catch {}
|
} catch { }
|
||||||
}
|
}
|
||||||
|
|
||||||
const parseNum = (v: unknown): number => {
|
const parseNum = (v: unknown): number => {
|
||||||
|
|
@ -575,73 +573,73 @@ export function ProcessInnovationPage() {
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
{loading || statsLoading
|
{loading || statsLoading
|
||||||
? // Loading skeleton for stats cards - matching new design
|
? // Loading skeleton for stats cards - matching new design
|
||||||
Array.from({ length: 4 }).map((_, index) => (
|
Array.from({ length: 4 }).map((_, index) => (
|
||||||
<Card
|
<Card
|
||||||
key={`skeleton-${index}`}
|
key={`skeleton-${index}`}
|
||||||
className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] backdrop-blur-sm rounded-2xl overflow-hidden"
|
className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] backdrop-blur-sm rounded-2xl overflow-hidden"
|
||||||
>
|
>
|
||||||
<CardContent className="p-2">
|
<CardContent className="p-2">
|
||||||
<div className="flex flex-col justify-between gap-2">
|
<div className="flex flex-col justify-between gap-2">
|
||||||
<div className="flex justify-between items-center border-b-2 mx-4 border-gray-500/20">
|
<div className="flex justify-between items-center border-b-2 mx-4 border-gray-500/20">
|
||||||
<div
|
<div
|
||||||
className="h-6 bg-gray-600 rounded animate-pulse"
|
className="h-6 bg-gray-600 rounded animate-pulse"
|
||||||
style={{ width: "60%" }}
|
style={{ width: "60%" }}
|
||||||
/>
|
/>
|
||||||
<div className="p-3 bg-emerald-500/20 rounded-full w-fit">
|
<div className="p-3 bg-emerald-500/20 rounded-full w-fit">
|
||||||
<div className="w-6 h-6 bg-gray-600 rounded animate-pulse" />
|
<div className="w-6 h-6 bg-gray-600 rounded animate-pulse" />
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-center flex-col p-1">
|
|
||||||
<div
|
|
||||||
className="h-8 bg-gray-600 rounded mb-1 animate-pulse"
|
|
||||||
style={{ width: "40%" }}
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
className="h-4 bg-gray-600 rounded animate-pulse"
|
|
||||||
style={{ width: "80%" }}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
<div className="flex items-center justify-center flex-col p-1">
|
||||||
</Card>
|
<div
|
||||||
))
|
className="h-8 bg-gray-600 rounded mb-1 animate-pulse"
|
||||||
|
style={{ width: "40%" }}
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className="h-4 bg-gray-600 rounded animate-pulse"
|
||||||
|
style={{ width: "80%" }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))
|
||||||
: statsCards.map((card) => (
|
: statsCards.map((card) => (
|
||||||
<Card
|
<Card
|
||||||
key={card.id}
|
key={card.id}
|
||||||
className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] backdrop-blur-sm border-gray-700/50"
|
className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] backdrop-blur-sm border-gray-700/50"
|
||||||
>
|
>
|
||||||
<CardContent className="p-2">
|
<CardContent className="p-2">
|
||||||
<div className="flex flex-col justify-between gap-2">
|
<div className="flex flex-col justify-between gap-2">
|
||||||
<div className="flex justify-between items-center border-b-2 mx-4 border-gray-500/20">
|
<div className="flex justify-between items-center border-b-2 mx-4 border-gray-500/20">
|
||||||
<h3 className="text-lg font-bold text-white font-persian">
|
<h3 className="text-lg font-bold text-white font-persian">
|
||||||
{card.title}
|
{card.title}
|
||||||
</h3>
|
</h3>
|
||||||
<div
|
<div
|
||||||
className={`p-3 gird placeitems-center rounded-full w-fit `}
|
className={`p-3 gird placeitems-center rounded-full w-fit `}
|
||||||
>
|
>
|
||||||
{card.icon}
|
{card.icon}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-center flex-col p-1">
|
|
||||||
<p
|
|
||||||
className={`text-3xl font-bold ${card.color} mb-1`}
|
|
||||||
>
|
|
||||||
{card.value}
|
|
||||||
</p>
|
|
||||||
<p className="text-sm text-gray-300 font-persian">
|
|
||||||
{card.description}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
<div className="flex items-center justify-center flex-col p-1">
|
||||||
</Card>
|
<p
|
||||||
))}
|
className={`text-3xl font-bold ${card.color} mb-1`}
|
||||||
|
>
|
||||||
|
{card.value}
|
||||||
|
</p>
|
||||||
|
<p className="text-sm text-gray-300 font-persian">
|
||||||
|
{card.description}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Process Impacts Chart */}
|
{/* Process Impacts Chart */}
|
||||||
<Card className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] backdrop-blur-sm rounded-2xl w-full overflow-hidden">
|
<Card className="bg-[linear-gradient(to_bottom_left,#464861,50%,#111628)] backdrop-blur-sm rounded-2xl w-full overflow-hidden">
|
||||||
<CardContent className="p-4">
|
<CardContent >
|
||||||
<CustomBarChart
|
<CustomBarChart
|
||||||
title="تاثیرات فرآیندی به صورت درصد مقایسه ای"
|
title="تاثیرات فرآیندی به صورت درصد مقایسه ای"
|
||||||
loading={statsLoading}
|
loading={statsLoading}
|
||||||
|
|
@ -842,8 +840,8 @@ export function ProcessInnovationPage() {
|
||||||
<div className="font-bold">
|
<div className="font-bold">
|
||||||
{formatNumber(
|
{formatNumber(
|
||||||
((stats.averageScore ?? 0) as number).toFixed?.(1) ??
|
((stats.averageScore ?? 0) as number).toFixed?.(1) ??
|
||||||
stats.averageScore ??
|
stats.averageScore ??
|
||||||
0,
|
0,
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -883,9 +881,9 @@ export function ProcessInnovationPage() {
|
||||||
<span className="text-white font-bold font-persian">
|
<span className="text-white font-bold font-persian">
|
||||||
{selectedProjectDetails?.start_date
|
{selectedProjectDetails?.start_date
|
||||||
? moment(
|
? moment(
|
||||||
selectedProjectDetails?.start_date,
|
selectedProjectDetails?.start_date,
|
||||||
"YYYY-MM-DD",
|
"YYYY-MM-DD",
|
||||||
).format("YYYY/MM/DD")
|
).format("YYYY/MM/DD")
|
||||||
: "-"}
|
: "-"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -898,9 +896,9 @@ export function ProcessInnovationPage() {
|
||||||
<span className="text-white font-bold font-persian">
|
<span className="text-white font-bold font-persian">
|
||||||
{selectedProjectDetails?.done_date
|
{selectedProjectDetails?.done_date
|
||||||
? moment(
|
? moment(
|
||||||
selectedProjectDetails?.done_date,
|
selectedProjectDetails?.done_date,
|
||||||
"YYYY-MM-DD",
|
"YYYY-MM-DD",
|
||||||
).format("YYYY/MM/DD")
|
).format("YYYY/MM/DD")
|
||||||
: "-"}
|
: "-"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -149,7 +149,7 @@ export function Sidebar({
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const autoExpandParents = () => {
|
const autoExpandParents = () => {
|
||||||
const newExpandedItems: string[] = [];
|
const newExpandedItems: string[] = [];
|
||||||
|
|
||||||
menuItems.forEach((item) => {
|
menuItems.forEach((item) => {
|
||||||
if (item.children) {
|
if (item.children) {
|
||||||
const hasActiveChild = item.children.some(
|
const hasActiveChild = item.children.some(
|
||||||
|
|
@ -160,10 +160,10 @@ export function Sidebar({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
setExpandedItems(newExpandedItems);
|
setExpandedItems(newExpandedItems);
|
||||||
};
|
};
|
||||||
|
|
||||||
autoExpandParents();
|
autoExpandParents();
|
||||||
}, [location.pathname]);
|
}, [location.pathname]);
|
||||||
|
|
||||||
|
|
@ -200,8 +200,8 @@ export function Sidebar({
|
||||||
|
|
||||||
const renderMenuItem = (item: MenuItem, level = 0) => {
|
const renderMenuItem = (item: MenuItem, level = 0) => {
|
||||||
const isActive = isActiveRoute(item.href, item.children);
|
const isActive = isActiveRoute(item.href, item.children);
|
||||||
const isExpanded = expandedItems.includes(item.id) ||
|
const isExpanded = expandedItems.includes(item.id) ||
|
||||||
(item.children && item.children.some(child =>
|
(item.children && item.children.some(child =>
|
||||||
child.href && location.pathname === child.href
|
child.href && location.pathname === child.href
|
||||||
));
|
));
|
||||||
const hasChildren = item.children && item.children.length > 0;
|
const hasChildren = item.children && item.children.length > 0;
|
||||||
|
|
@ -265,14 +265,14 @@ export function Sidebar({
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
className={cn(
|
className={cn(
|
||||||
"w-full text-right",
|
"w-full text-right",
|
||||||
// Disable pointer cursor when child is active (cannot collapse)
|
// Disable pointer cursor when child is active (cannot collapse)
|
||||||
item.children && item.children.some(child =>
|
item.children && item.children.some(child =>
|
||||||
child.href && location.pathname === child.href
|
child.href && location.pathname === child.href
|
||||||
) && "cursor-not-allowed"
|
) && "cursor-not-allowed"
|
||||||
)}
|
)}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|
@ -313,7 +313,7 @@ export function Sidebar({
|
||||||
"w-4 h-4 transition-transform duration-200",
|
"w-4 h-4 transition-transform duration-200",
|
||||||
isExpanded ? "rotate-180" : "rotate-0",
|
isExpanded ? "rotate-180" : "rotate-0",
|
||||||
// Show different color when child is active (cannot collapse)
|
// Show different color when child is active (cannot collapse)
|
||||||
item.children && item.children.some(child =>
|
item.children && item.children.some(child =>
|
||||||
child.href && location.pathname === child.href
|
child.href && location.pathname === child.href
|
||||||
) ? "text-emerald-400" : "text-current"
|
) ? "text-emerald-400" : "text-current"
|
||||||
)}
|
)}
|
||||||
|
|
@ -361,7 +361,7 @@ export function Sidebar({
|
||||||
{!isCollapsed ? (
|
{!isCollapsed ? (
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<GalleryVerticalEnd
|
<GalleryVerticalEnd
|
||||||
color="black"
|
color="black"
|
||||||
size={32}
|
size={32}
|
||||||
strokeWidth={1}
|
strokeWidth={1}
|
||||||
className="bg-green-400 p-1.5 rounded-lg"
|
className="bg-green-400 p-1.5 rounded-lg"
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ export function CustomBarChart({
|
||||||
// Loading skeleton
|
// Loading skeleton
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<div className={`space-y-6 ${className}`} style={{ height }}>
|
<div className={`space-y-6 p-4 ${className}`} style={{ height }}>
|
||||||
{title && (
|
{title && (
|
||||||
<div className="h-7 bg-gray-600 rounded animate-pulse mb-4 w-1/2"></div>
|
<div className="h-7 bg-gray-600 rounded animate-pulse mb-4 w-1/2"></div>
|
||||||
)}
|
)}
|
||||||
|
|
@ -68,13 +68,15 @@ export function CustomBarChart({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`space-y-6 ${className}`} style={{ height }}>
|
<div className={`space-y-6 ${className}`} style={{ height }}>
|
||||||
{title && (
|
<div className="border-b">
|
||||||
<h3 className="text-xl font-bold text-white font-persian text-right mb-4">
|
{title && (
|
||||||
{title}
|
<h3 className="text-xl font-bold text-white font-persian text-right p-4">
|
||||||
</h3>
|
{title}
|
||||||
)}
|
</h3>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4 px-4 pb-4">
|
||||||
{data.map((item, index) => {
|
{data.map((item, index) => {
|
||||||
const percentage =
|
const percentage =
|
||||||
globalMaxValue > 0 ? (item.value / globalMaxValue) * 100 : 0;
|
globalMaxValue > 0 ? (item.value / globalMaxValue) * 100 : 0;
|
||||||
|
|
@ -84,9 +86,8 @@ export function CustomBarChart({
|
||||||
<div key={index} className="flex items-center gap-3">
|
<div key={index} className="flex items-center gap-3">
|
||||||
{/* Label */}
|
{/* Label */}
|
||||||
<span
|
<span
|
||||||
className={`font-persian text-sm min-w-[160px] text-right ${
|
className={`font-persian text-sm min-w-[160px] text-right ${item.labelColor || "text-white"
|
||||||
item.labelColor || "text-white"
|
}`}
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
{item.label}
|
{item.label}
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -96,9 +97,8 @@ export function CustomBarChart({
|
||||||
className={`flex-1 flex items-center bg-gray-700 rounded-full relative overflow-hidden ${barHeight}`}
|
className={`flex-1 flex items-center bg-gray-700 rounded-full relative overflow-hidden ${barHeight}`}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`${barHeight} rounded-full transition-all duration-700 ease-out relative ${
|
className={`${barHeight} rounded-full transition-all duration-700 ease-out relative ${item.color || "bg-emerald-400"
|
||||||
item.color || "bg-emerald-400"
|
}`}
|
||||||
}`}
|
|
||||||
style={{
|
style={{
|
||||||
width: `${Math.min(percentage, 100)}%`,
|
width: `${Math.min(percentage, 100)}%`,
|
||||||
}}
|
}}
|
||||||
|
|
@ -110,19 +110,18 @@ export function CustomBarChart({
|
||||||
|
|
||||||
{/* Value Label */}
|
{/* Value Label */}
|
||||||
<span
|
<span
|
||||||
className={`font-bold text-sm min-w-[60px] text-left ${
|
className={`font-bold text-sm min-w-[60px] text-left ${item.color?.includes("emerald")
|
||||||
item.color?.includes("emerald")
|
? "text-emerald-400"
|
||||||
? "text-emerald-400"
|
: item.color?.includes("blue")
|
||||||
: item.color?.includes("blue")
|
? "text-blue-400"
|
||||||
? "text-blue-400"
|
: item.color?.includes("purple")
|
||||||
: item.color?.includes("purple")
|
? "text-purple-400"
|
||||||
? "text-purple-400"
|
: item.color?.includes("red")
|
||||||
: item.color?.includes("red")
|
? "text-red-400"
|
||||||
? "text-red-400"
|
: item.color?.includes("yellow")
|
||||||
: item.color?.includes("yellow")
|
? "text-yellow-400"
|
||||||
? "text-yellow-400"
|
: "text-emerald-400"
|
||||||
: "text-emerald-400"
|
}`}
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
{item.valuePrefix || ""}
|
{item.valuePrefix || ""}
|
||||||
{formatNumber(parseFloat(displayValue))}
|
{formatNumber(parseFloat(displayValue))}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ const Table = React.forwardRef<HTMLTableElement, TableProps>(
|
||||||
<div className={cn("relative w-full", containerClassName)}>
|
<div className={cn("relative w-full", containerClassName)}>
|
||||||
<table
|
<table
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn("w-full caption-bottom text-sm", className)}
|
className={cn("w-full caption-bottom text-sm h-full", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,18 @@ export default [
|
||||||
route("login", "routes/login.tsx"),
|
route("login", "routes/login.tsx"),
|
||||||
route("dashboard", "routes/dashboard.tsx"),
|
route("dashboard", "routes/dashboard.tsx"),
|
||||||
route("dashboard/project-management", "routes/project-management.tsx"),
|
route("dashboard/project-management", "routes/project-management.tsx"),
|
||||||
route("dashboard/innovation-basket/process-innovation", "routes/innovation-basket.process-innovation.tsx"),
|
route(
|
||||||
route("dashboard/innovation-basket/green-innovation", "routes/green-innovation.tsx"),
|
"dashboard/innovation-basket/process-innovation",
|
||||||
|
"routes/innovation-basket.process-innovation.tsx"
|
||||||
|
),
|
||||||
|
route(
|
||||||
|
"dashboard/innovation-basket/green-innovation",
|
||||||
|
"routes/green-innovation.tsx"
|
||||||
|
),
|
||||||
|
route(
|
||||||
|
"/dashboard/innovation-basket/digital-innovation",
|
||||||
|
"routes/digital-innovation-page.tsx"
|
||||||
|
),
|
||||||
route("projects", "routes/projects.tsx"),
|
route("projects", "routes/projects.tsx"),
|
||||||
route("dashboard/ecosystem", "routes/ecosystem.tsx"),
|
route("dashboard/ecosystem", "routes/ecosystem.tsx"),
|
||||||
route("404", "routes/404.tsx"),
|
route("404", "routes/404.tsx"),
|
||||||
|
|
|
||||||
17
app/routes/digital-innovation-page.tsx
Normal file
17
app/routes/digital-innovation-page.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { ProtectedRoute } from "~/components/auth/protected-route";
|
||||||
|
import DigitalInnovationPage from "~/components/dashboard/project-management/digital-innovation-page";
|
||||||
|
|
||||||
|
export function meta() {
|
||||||
|
return [
|
||||||
|
{ title: "نوآوری در فرآیند - سیستم مدیریت فناوری و نوآوری" },
|
||||||
|
{ name: "description", content: "مدیریت پروژههای نوآوری در فرآیند" },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ProcessInnovation() {
|
||||||
|
return (
|
||||||
|
<ProtectedRoute requireAuth={true}>
|
||||||
|
<DigitalInnovationPage />
|
||||||
|
</ProtectedRoute>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,7 @@ import type { Route } from "./+types/project-management";
|
||||||
import { ProjectManagementPage } from "~/components/dashboard/project-management/project-management-page";
|
import { ProjectManagementPage } from "~/components/dashboard/project-management/project-management-page";
|
||||||
import { ProtectedRoute } from "~/components/auth/protected-route";
|
import { ProtectedRoute } from "~/components/auth/protected-route";
|
||||||
|
|
||||||
export function meta({}: Route.MetaArgs) {
|
export function meta({ }: Route.MetaArgs) {
|
||||||
return [
|
return [
|
||||||
{ title: "مدیریت پروژهها - سیستم مدیریت فناوری و نوآوری" },
|
{ title: "مدیریت پروژهها - سیستم مدیریت فناوری و نوآوری" },
|
||||||
{ name: "description", content: "مدیریت و نظارت بر پروژههای فناوری و نوآوری" },
|
{ name: "description", content: "مدیریت و نظارت بر پروژههای فناوری و نوآوری" },
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "react-router build",
|
"build": "react-router build",
|
||||||
"dev": "react-router dev --port 3000",
|
"dev": "react-router dev",
|
||||||
"start": "react-router-serve ./build/server/index.js",
|
"start": "react-router-serve ./build/server/index.js",
|
||||||
"typecheck": "react-router typegen && tsc"
|
"typecheck": "react-router typegen && tsc"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user