آماده سازی برای دو پتروشیمی دیگر

This commit is contained in:
mahmoodsht 2025-11-03 12:48:22 +03:30
parent 69e702d368
commit e297522e5c
10 changed files with 391 additions and 57 deletions

View File

@ -212,7 +212,9 @@ export function LoginForm({ onSuccess }: LoginFormProps) {
{/* Right Side - Branding */} {/* Right Side - Branding */}
<LoginSidebar> <LoginSidebar>
<LoginBranding <LoginBranding
brandName="پتروشیمی بندر امام" brandName="پتروشیمی آپادانا"
// brandName="پتروشیمی نوری"
// brandName="پتروشیمی بندر امام"
engSub="Inception by Fara" engSub="Inception by Fara"
companyName="توسعه‌یافته توسط شرکت رهپویان دانش و فناوری فرا" companyName="توسعه‌یافته توسط شرکت رهپویان دانش و فناوری فرا"
logo={<img src="/brand2.svg"/>} logo={<img src="/brand2.svg"/>}

View File

@ -1,6 +1,7 @@
import React from "react"; import React from "react";
import { cn } from "~/lib/utils"; import { cn } from "~/lib/utils";
interface LoginLayoutProps { interface LoginLayoutProps {
children: React.ReactNode; children: React.ReactNode;
className?: string; className?: string;
@ -106,14 +107,25 @@ export function LoginBranding({
}: LoginBrandingProps) { }: LoginBrandingProps) {
return ( return (
<> <>
{/* Top Logo */} <div className="flex justify-end">
<div className="flex justify-end">
<div className="text-slate-800 font-persian"> <div className="text-slate-800 font-persian">
<div className="text-lg font-bold leading-tight"> <div className="text-lg font-bold leading-tight">
<img src="/brand.svg" /> <img
</div> src="/brand.svg?v=1"
alt="Brand Logo"
className="w-auto h-16" // اضافه کردن سایز مشخص
onError={(e) => {
e.target.style.display = 'none';
console.log('Image failed to load');
}}
/>
</div> </div>
</div> </div>
</div>
{/* Bottom Section */} {/* Bottom Section */}
<div className="flex flex-col gap-2 mb-4 items-end justify-end"> <div className="flex flex-col gap-2 mb-4 items-end justify-end">

View File

@ -1,3 +1,4 @@
//این فایل مخصوص
//شماتیک آپادانا //شماتیک آپادانا
import React from "react"; import React from "react";

View File

@ -1,4 +1,4 @@
//این فایل مخصوص
//شماتیک بندر امام //شماتیک بندر امام
import React from "react"; import React from "react";
import { formatNumber } from "~/lib/utils"; import { formatNumber } from "~/lib/utils";

View File

@ -0,0 +1,211 @@
//این فایل مخصوص
//شماتیک نوری
import React from "react";
import { formatNumber } from "~/lib/utils";
export type CompanyInfo = {
id: string;
imageUrl: string;
name: string;
costReduction: number;
revenue?: number;
capacity?: number;
costI: number;
capacityI: number;
revenueI: number;
cost: number | string;
};
export type D3ImageInfoProps = {
companies: CompanyInfo[];
width?: number;
height?: number;
};
const InfoBox = ({ company, style }: { company: CompanyInfo; style: any }) => {
// const hideCapacity = company.name === "واحد 300"; // اگر واحد 300 بود ظرفیت مخفی شود
const hideCapacity = false;
return (
<div className={`info-box`} style={style}>
<div className="info-box-content">
<div className="info-row">
<div className="info-label">درآمد:</div>
<div className="info-value revenue text-[12px]">{formatNumber(company?.revenue || 0)}</div>
<div className="info-unit">میلیون ریال</div>
</div>
<div className="info-row">
<div className="info-label">هزینه:</div>
{hideCapacity ? (
<div className="info-value cost2 text-[12px]">{formatNumber(company?.cost || 0)}</div>
) : (
<div className="info-value cost text-[12px]">{formatNumber(company?.cost || 0)}</div>
)}
<div className="info-unit">میلیون ریال</div>
</div>
{!hideCapacity && (
<div className="info-row">
<div className="info-label">ظرفیت:</div>
<div className="info-value capacity text-[12px]">{formatNumber(company?.capacity || 0)}</div>
<div className="info-unit">تن در سال</div>
</div>
)}
</div>
</div>
);
};
export function D3ImageInfo({ companies }: D3ImageInfoProps) {
// واحدهای جدید - 4 واحد
const sample = [
{ id: "واحد 100", name: "واحد 100", imageUrl: "/abniro.png" },
{ id: "واحد 200", name: "واحد 200", imageUrl: "/besparan.png" },
{ id: "واحد 300", name: "واحد 300", imageUrl: "/khwarazmi.png" },
{ id: "واحد 400", name: "واحد 400", imageUrl: "/faravash1.png" }
];
const merged = sample.map(company => {
const found = companies.find(item => item.id === company.id);
return found
? found
: { ...company, cost: 0, capacity: 0, revenue: 0, costReduction: 0, costI: 0, capacityI: 0, revenueI: 0 };
});
const displayCompanies = merged;
console.log(displayCompanies);
// موقعیت‌های جدید برای چیدمان لوزی شکل (3 ردیف - 1-2-1)
// گرید 5x4 نگه داشته شده اما موقعیت‌ها تغییر کرده
const gridPositions = [
{ col: 2, row: 1, colI: 1, rowI: 1, name: "واحد 100" }, // ردیف اول - ستون اول
{ col: 4, row: 1, colI: 5, rowI: 1, name: "واحد 200" }, // ردیف اول - ستون دوم
{ col: 2, row: 3, colI: 1, rowI: 3, name: "واحد 300" }, // ردیف دوم - ستون اول
{ col: 4, row: 3, colI: 5, rowI: 3, name: "واحد 400" }, // ردیف دوم - ستون دوم
];
return (
<div className="w-full h-[500px] rounded-xl">
<div dir="ltr" className="company-grid-container">
{displayCompanies.map((company, index) => {
const gp = gridPositions.find(v => v.name === company.name);
return (
<React.Fragment key={company.id}>
<div
className={`company-item`}
style={{ gridColumn: gp?.col, gridRow: gp?.row }}
>
<div className="company-image-container">
<img
src={company.imageUrl}
alt={company.name}
className="company-image"
/>
</div>
{company.name}
</div>
<InfoBox company={company} style={{ gridColumn: gp?.colI, gridRow: gp?.rowI }} />
</React.Fragment>
);
})}
</div>
<style jsx>{`
.company-grid-container {
display: grid;
grid-template-columns: repeat(5, 1fr);
grid-template-rows: repeat(4, 1fr);
gap: 5px;
width: 100%;
height: 500px;
}
.company-item {
border-radius: 8px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.company-image-container {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
}
.company-image {
object-fit: contain;
height: 100px;
}
.info-box {
border: 1px solid #3F415A;
border-radius: 10px;
height: max-content;
align-self: center;
justify-self: center;
padding: .2rem 1.2rem;
min-width: 8rem;
background-color: transparent;
}
.info-box-content {
display: flex;
flex-direction: column;
justify-content: center;
}
.info-row {
position: relative;
margin: .1rem 0;
display: flex;
gap: .5rem;
justify-content: space-between;
direction: rtl;
}
.info-row:has(.info-value.revenue) {
border-bottom: 1px solid #3AEA83;
}
.info-row:has(.info-value.cost) {
border-bottom: 1px solid #F76276;
}
.info-label {
color: #FFFFFF;
font-size: 11px;
font-weight: 300;
text-align: right;
margin: auto 0;
}
.info-value {
color: #34D399;
font-size: 14px;
font-weight: 500;
text-align: right;
margin-bottom: .5rem;
}
.info-value.revenue { color: #fff; }
.info-value.cost { color: #fff; }
.info-value.cost2 { color: #fff; }
.info-value.capacity { color: #fff; }
.info-unit {
position: absolute;
left: 0;
bottom: 2px;
color: #ACACAC;
font-size: 6px;
font-weight: 400;
}
`}</style>
</div>
);
}

View File

@ -641,6 +641,7 @@ export function DashboardHome() {
<TabsContent value="canvas" className="w-ful h-full"> <TabsContent value="canvas" className="w-ful h-full">
<div className="p-4 h-full w-full"> <div className="p-4 h-full w-full">
<D3ImageInfo <D3ImageInfo
//پتروشیمی بندر امام //پتروشیمی بندر امام
// companies={companyChartData.map((item) => { // companies={companyChartData.map((item) => {
// const imageMap: Record<string, string> = { // const imageMap: Record<string, string> = {
@ -662,7 +663,14 @@ export function DashboardHome() {
"واحد 400": "/faravash1.png" "واحد 400": "/faravash1.png"
}; };
//پتروشیمی نوری
// companies={companyChartData.map((item) => {
// const imageMap: Record<string, string> = {
// "واحد 100": "/abniro.png" ,
// "واحد 200": "/besparan.png" ,
// "واحد 300": "/khwarazmi.png" ,
// "واحد 400": "/faravash1.png"
// };

View File

@ -233,7 +233,14 @@ export function Header({
const redirectHandler = async () => { const redirectHandler = async () => {
try { try {
const getData = await apiService.post("/GenerateSsoCode"); const getData = await apiService.post("/GenerateSsoCode");
const url = `https://inogen-bpms.pelekan.org/redirect/${getData.data}`;
//بندر امام
// const url = `https://inogen-bpms.pelekan.org/redirect/${getData.data}`;
//آپادانا
const url = `https://APADANA-IATM-bpms.pelekan.org/redirect/${getData.data}`;
//نوری
// const url = `https://NOPC-IATM-bpms.pelekan.org/redirect/${getData.data}`;
window.open(url, "_blank"); window.open(url, "_blank");
} catch (error) { } catch (error) {
console.log(error); console.log(error);

View File

@ -7,7 +7,13 @@ import { useAuth } from "../../contexts/auth-context";
import apiService from "../../lib/api"; import apiService from "../../lib/api";
const API_BASE_URL = const API_BASE_URL =
import.meta.env.VITE_API_URL || "https://inogen-back.pelekan.org/api"; //بندر امام
// import.meta.env.VITE_API_URL || "https://inogen-back.pelekan.org/api";
//آپادانا
import.meta.env.VITE_API_URL || "https://APADANA-IATM-back.pelekan.org/api";
//نوری
// import.meta.env.VITE_API_URL || "https://NOPC-IATM-back.pelekan.org/api";
export interface Node { export interface Node {
id: string; id: string;
@ -143,7 +149,9 @@ export function NetworkGraph({
// نود مرکزی // نود مرکزی
const centerNode: Node = { const centerNode: Node = {
id: "center", id: "center",
label: "پتروشیمی بندر امام", // label: "پتروشیمی بندر امام",
// label: "پتروشیمی نوری",
label: "پتروشیمی آپادانا",
category: "center", category: "center",
stageid: 0, stageid: 0,
isCenter: true, isCenter: true,
@ -367,13 +375,62 @@ export function NetworkGraph({
nodeGroup.each(function (d) { nodeGroup.each(function (d) {
const group = d3.select(this); const group = d3.select(this);
if (d.isCenter) { // if (d.isCenter) {
// const rect = group
// .append("rect")
// .attr("width", 200)
// .attr("height", 80)
// .attr("x", -100) // نصف عرض جدید منفی
// .attr("y", -40) // نصف ارتفاع جدید منفی
// .attr("rx", 8)
// .attr("ry", 8)
// .attr("fill", categoryToColor[d.category] || "#94A3B8")
// .attr("stroke", "#FFFFFF")
// .attr("stroke-width", 3)
// .style("pointer-events", "none");
// if (d.imageUrl || d.isCenter) {
// const pattern = defs
// .append("pattern")
// .attr("id", `image-${d.id}`)
// .attr("x", 0)
// .attr("y", 0)
// .attr("width", 1)
// .attr("height", 1);
// pattern
// .append("image")
// .attr("x", 0)
// .attr("y", 0)
// .attr("width", 200) // ← هم‌اندازه با مستطیل
// .attr("height", 80)
// .attr("href", d.isCenter ? "/main-circle.png" : d.imageUrl)
// .attr("preserveAspectRatio", "xMidYMid slice");
// rect.attr("fill", `url(#image-${d.id})`);
// }
// }
// راه حل ساده‌تر - ابعاد ثابت با حفظ نسبت
if (d.isCenter) {
//آپادانا
const fixedWidth = 198;
const fixedHeight = 200; // یا می‌توانید براساس نسبت تصویر محاسبه کنید
//بندر امام
// const fixedWidth = 100;
// const fixedHeight = 80; // یا می‌توانید براساس نسبت تصویر محاسبه کنید
//نوری
// const fixedWidth = 100;
// const fixedHeight = 80; // یا می‌توانید براساس نسبت تصویر محاسبه کنید
const rect = group const rect = group
.append("rect") .append("rect")
.attr("width", 200) .attr("width", fixedWidth)
.attr("height", 80) .attr("height", fixedHeight)
.attr("x", -100) // نصف عرض جدید منفی .attr("x", -fixedWidth / 2)
.attr("y", -40) // نصف ارتفاع جدید منفی .attr("y", -fixedHeight / 2)
.attr("rx", 8) .attr("rx", 8)
.attr("ry", 8) .attr("ry", 8)
.attr("fill", categoryToColor[d.category] || "#94A3B8") .attr("fill", categoryToColor[d.category] || "#94A3B8")
@ -381,7 +438,6 @@ export function NetworkGraph({
.attr("stroke-width", 3) .attr("stroke-width", 3)
.style("pointer-events", "none"); .style("pointer-events", "none");
if (d.imageUrl || d.isCenter) {
const pattern = defs const pattern = defs
.append("pattern") .append("pattern")
.attr("id", `image-${d.id}`) .attr("id", `image-${d.id}`)
@ -394,14 +450,15 @@ export function NetworkGraph({
.append("image") .append("image")
.attr("x", 0) .attr("x", 0)
.attr("y", 0) .attr("y", 0)
.attr("width", 200) // ← هم‌اندازه با مستطیل .attr("width", fixedWidth)
.attr("height", 80) .attr("height", fixedHeight)
.attr("href", d.isCenter ? "/main-circle.png" : d.imageUrl) .attr("href", d.isCenter ? "/main-circle.png" : d.imageUrl)
.attr("preserveAspectRatio", "xMidYMid slice"); .attr("preserveAspectRatio", "xMidYMid meet"); // حفظ نسبت تصویر
rect.attr("fill", `url(#image-${d.id})`); rect.attr("fill", `url(#image-${d.id})`);
}
} else { }
else {
const circle = group const circle = group
.append("circle") .append("circle")
.attr("r", 25) .attr("r", 25)
@ -444,7 +501,22 @@ export function NetworkGraph({
.append("text") .append("text")
.text((d) => d.label) .text((d) => d.label)
.attr("text-anchor", "middle") .attr("text-anchor", "middle")
.attr("dy", (d) => (d.isCenter ? 50 : 45)) .attr("dy", (d) => {
if (d.isCenter) {
//آپادانا
const centerNodeHeight = 200; // ارتفاع نود مرکزی
//بندر امام
// const centerNodeHeight = 80; // ارتفاع نود مرکزی
//نوری
// const centerNodeHeight = 80; // ارتفاع نود مرکزی
return centerNodeHeight / 2 + 20; // نصف ارتفاع + فاصله 20px
}
return 45; // برای نودهای دیگر
})
.attr("font-size", (d) => (d.isCenter ? "14px" : "12px")) .attr("font-size", (d) => (d.isCenter ? "14px" : "12px"))
.attr("font-weight", "bold") .attr("font-weight", "bold")
.attr("fill", "#F9FAFB") .attr("fill", "#F9FAFB")

View File

@ -162,10 +162,24 @@ class ApiService {
// Innovation process function call wrapper // Innovation process function call wrapper
public async call<T = any>(payload: any) { public async call<T = any>(payload: any) {
//بندر امام
const url = "https://inogen-back.pelekan.org/api/call"; const url = "https://inogen-back.pelekan.org/api/call";
//آپادانا
const url = "https://APADANA-IATM-back.pelekan.org/api/call";
//نوری
const url = "https://NOPC-IATM-back.pelekan.org/api/call";
return this.postAbsolute<T>(url, payload); return this.postAbsolute<T>(url, payload);
} }
const API_BASE_URL =
//بندر امام
// import.meta.env.VITE_API_URL || "https://inogen-bpms-back.pelekan.org/api";
//آپادانا
import.meta.env.VITE_API_URL || "https://APADANA-IATM-back.pelekan.org/api";
//نوری
// import.meta.env.VITE_API_URL || "https://NOPC-IATM-back.pelekan.org/api";
// GET request // GET request
public async get<T = any>(endpoint: string): Promise<ApiResponse<T>> { public async get<T = any>(endpoint: string): Promise<ApiResponse<T>> {
return this.request<T>(endpoint, { return this.request<T>(endpoint, {

View File

@ -16,7 +16,14 @@ import type { Route } from "./+types/ecosystem";
// Get API base URL at module level to avoid process.env access in browser // Get API base URL at module level to avoid process.env access in browser
const API_BASE_URL = const API_BASE_URL =
import.meta.env.VITE_API_URL || "https://inogen-back.pelekan.org/api"; //بندر امام
// import.meta.env.VITE_API_URL || "https://inogen-back.pelekan.org/api";
//آپادانا
import.meta.env.VITE_API_URL || "https://APADANA-IATM-back.pelekan.org/api";
//نوری
// import.meta.env.VITE_API_URL || "https://NOPC-IATM-back.pelekan.org/api";
// Import the CompanyDetails type // Import the CompanyDetails type
import { Hexagon } from "lucide-react"; import { Hexagon } from "lucide-react";