fix conflict

This commit is contained in:
yaschalew
2026-05-29 19:46:18 +03:00
148 changed files with 6507 additions and 12683 deletions

View File

@@ -1,7 +1,6 @@
import { Navigate, Outlet, Route, Routes, useLocation, useNavigate } from "react-router-dom";
import { DashboardLayout, type SidebarItem } from "@edr/ui-common";
import { LayoutDashboard, Network, Paperclip, Settings } from "lucide-react";
import { useAuth } from "./auth/useAuth";
import LoginPage from "./pages/auth/LoginPage";
import OverviewPage from "./pages/dashboard/OverviewPage";
@@ -12,6 +11,8 @@ import UserManagementPage from "./pages/dashboard/user-management/UserManagement
import LoadingScreen from "./components/LoadingScreen";
import FileUploadSettingsPage from "./pages/documents/FileUploadSettingsPage";
import DropdownSettingsPage from "./pages/dropdown_settings/DropdownSettingsPage";
import DemoUser1Page from "./pages/dashboard/demo/DemoUser1Page";
import DemoUser2Page from "./pages/dashboard/demo/DemoUser2Page";
const sidebarItems: SidebarItem[] = [
{
@@ -50,11 +51,73 @@ const sidebarItems: SidebarItem[] = [
}
];
const hasPermission = (
user: ReturnType<typeof useAuth>["user"],
key: string,
) => {
if (!user) return false;
if (user.permissions?.some((p) => p.key === key)) return true;
return (user.employee ?? []).some((emp) =>
(emp.positions ?? []).some((pos) =>
(pos.permissions ?? []).some((p) => p.key === key),
),
);
};
const DashboardShell = () => {
const navigate = useNavigate();
const location = useLocation();
const { user, logout } = useAuth();
const sidebarItems: SidebarItem[] = [
{
label: "Overview",
href: "/dashboard/overview",
icon: <LayoutDashboard />,
},
{
label: "User management",
href: "/dashboard/user-management",
icon: <Network />,
children: [
{
label: "Employees",
href: "/dashboard/user-management/employees",
},
{
label: "Permissions",
href: "/dashboard/user-management/permissions",
},
{
label: "Roles",
href: "/dashboard/user-management/roles",
},
],
},
{
label: "Rule Engine",
href: "/dashboard/rule-engine",
icon: <Settings />,
},
...(hasPermission(user, "can:demo:user1")
? ([
{
label: "User1",
href: "/dashboard/user1",
icon: <Settings />,
},
] as SidebarItem[])
: []),
...(hasPermission(user, "can:demo:user2")
? ([
{
label: "User2",
href: "/dashboard/user2",
icon: <Settings />,
},
] as SidebarItem[])
: []),
];
const displayName = user?.name?.en || user?.username || user?.email || "User";
return (
@@ -98,9 +161,12 @@ const App = () => {
<Route path="user-management" element={<UserManagementPage />} />
<Route path="file-settings" element={<FileUploadSettingsPage />} />
<Route path="dropdown-settings" element={<DropdownSettingsPage />} />
<Route path="rule-engine" element={<RuleEnginePage />} />
<Route path="user-management/employees" element={<EmployeesPage />} />
<Route path="user-management/permissions" element={<PermissionsPage />} />
<Route path="user-management/roles" element={<RolesPage />} />
<Route path="user1" element={<DemoUser1Page />} />
<Route path="user2" element={<DemoUser2Page />} />
<Route path="org-structure" element={<Navigate to="/dashboard/user-management" replace />} />
<Route path="org-structure/*" element={<Navigate to="/dashboard/user-management" replace />} />
</Route>

View File

@@ -0,0 +1,221 @@
import { JSXElementConstructor, MouseEventHandler, ReactElement, ReactNode, SetStateAction, useState } from 'react';
export const ContractTypePage = () => {
const [expandedSections, setExpandedSections] = useState({
contractType: true,
serviceType: false,
cargoType: false
});
const [contractTypes, setContractTypes] = useState([
{ id: 1, name: 'Shipper', description: 'Company that sends the freight' },
{ id: 2, name: 'Consignee', description: 'Company that receives the freight' },
{ id: 3, name: 'Third Party', description: 'Company that is neither the shipper nor the consignee but is involved in the freight process' }
]);
const [serviceTypes, setServiceTypes] = useState([
{ id: 1, name: 'Standard', description: 'Regular shipping service' },
{ id: 2, name: 'Express', description: 'Fast delivery service' },
{ id: 3, name: 'Economy', description: 'Cost-effective shipping option' }
]);
const [cargoTypes, setCargoTypes] = useState([
{ id: 1, name: 'General Cargo', description: 'Standard packaged goods' },
{ id: 2, name: 'Temperature Controlled', description: 'Goods requiring specific temperature' },
{ id: 3, name: 'Hazardous Materials', description: 'Dangerous goods requiring special handling' }
]);
const [newContractType, setNewContractType] = useState({ name: '', description: '' });
const [newServiceType, setNewServiceType] = useState({ name: '', description: '' });
const [newCargoType, setNewCargoType] = useState({ name: '', description: '' });
const [showAddForms, setShowAddForms] = useState({
contractType: false,
serviceType: false,
cargoType: false
});
type SectionKey = 'contractType' | 'serviceType' | 'cargoType';
const toggleSection = (section: SectionKey) => {
setExpandedSections(prev => ({
...prev,
[section]: !prev[section]
}));
};
const toggleAddForm = (section: SectionKey) => {
setShowAddForms(prev => ({
...prev,
[section]: !prev[section]
}));
};
const handleAddContractType = () => {
if (newContractType.name && newContractType.description) {
setContractTypes([
...contractTypes,
{ id: Date.now(), ...newContractType }
]);
setNewContractType({ name: '', description: '' });
toggleAddForm('contractType');
}
};
const handleAddServiceType = () => {
if (newServiceType.name && newServiceType.description) {
setServiceTypes([
...serviceTypes,
{ id: Date.now(), ...newServiceType }
]);
setNewServiceType({ name: '', description: '' });
toggleAddForm('serviceType');
}
};
const handleAddCargoType = () => {
if (newCargoType.name && newCargoType.description) {
setCargoTypes([
...cargoTypes,
{ id: Date.now(), ...newCargoType }
]);
setNewCargoType({ name: '', description: '' });
toggleAddForm('cargoType');
}
};
const handleDelete = (type: string, id: number) => {
if (type === 'contract') {
setContractTypes(contractTypes.filter(item => item.id !== id));
} else if (type === 'service') {
setServiceTypes(serviceTypes.filter(item => item.id !== id));
} else if (type === 'cargo') {
setCargoTypes(cargoTypes.filter(item => item.id !== id));
}
};
const handleEdit = (type: any, id: any) => {
// Implement edit functionality as needed
alert(`Edit ${type} type with id: ${id}`);
};
const renderTable = (title: string | number | boolean | ReactElement<any, string | JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined, types: any[], onAdd: { (): void; (): void; (): void; }, newItem: { name: any; description: any; }, setNewItem: { (value: SetStateAction<{ name: string; description: string; }>): void; (value: SetStateAction<{ name: string; description: string; }>): void; (value: SetStateAction<{ name: string; description: string; }>): void; (arg0: any): void; }, showAddForm: boolean, typeKey: string, addHandler: MouseEventHandler<HTMLButtonElement> | undefined) => (
<div style={{ marginBottom: '20px' }}>
<div
style={{
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
padding: '10px',
backgroundColor: '#f0f0f0',
marginBottom: '10px'
}}
onClick={() => toggleSection(typeKey)}
>
<span style={{ marginRight: '10px', fontSize: '20px', color: '#138a49' }}>
{expandedSections[typeKey] ? '▼' : '▶'}
</span>
<h3 style={{ margin: 0 }}>{title}</h3>
</div>
{expandedSections[typeKey] && (
<div style={{ marginLeft: '20px' }}>
<button onClick={() => toggleAddForm(typeKey)}>
Add {title.replace(' Types', ' type')}
</button>
{showAddForm && (
<div style={{
marginTop: '10px',
marginBottom: '10px',
padding: '10px',
border: '1px solid #ccc',
borderRadius: '4px'
}}>
<h4>Add New {title.replace(' Types', '')}</h4>
<div>
<input
type="text"
placeholder="Name"
value={newItem.name}
onChange={(e) => setNewItem({ ...newItem, name: e.target.value })}
style={{ marginRight: '10px', padding: '5px' }}
/>
<input
type="text"
placeholder="Description"
value={newItem.description}
onChange={(e) => setNewItem({ ...newItem, description: e.target.value })}
style={{ marginRight: '10px', padding: '5px' }}
/>
<button onClick={addHandler}>Save</button>
<button onClick={() => toggleAddForm(typeKey)} style={{ marginLeft: '5px' }}>Cancel</button>
</div>
</div>
)}
<table style={{ width: '100%', borderCollapse: 'collapse', marginTop: '10px' }}>
<thead>
<tr style={{ backgroundColor: '#f2f2f2' }}>
<th style={{ border: '1px solid #ddd', padding: '8px', textAlign: 'left' }}>ID</th>
<th style={{ border: '1px solid #ddd', padding: '8px', textAlign: 'left' }}>Name</th>
<th style={{ border: '1px solid #ddd', padding: '8px', textAlign: 'left' }}>Description</th>
<th style={{ border: '1px solid #ddd', padding: '8px', textAlign: 'left' }}>Actions</th>
</tr>
</thead>
<tbody>
{types.map((type) => (
<tr key={type.id}>
<td style={{ border: '1px solid #ddd', padding: '8px' }}>{type.id}</td>
<td style={{ border: '1px solid #ddd', padding: '8px' }}>{type.name}</td>
<td style={{ border: '1px solid #ddd', padding: '8px' }}>{type.description}</td>
<td style={{ border: '1px solid #ddd', padding: '8px' }}>
<button onClick={() => handleEdit(typeKey, type.id)} style={{ marginRight: '5px' }}>Edit</button>
<button onClick={() => handleDelete(typeKey === 'contractType' ? 'contract' : typeKey === 'serviceType' ? 'service' : 'cargo', type.id)}>Delete</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
);
return (
<div>
{renderTable(
'Contract Types',
contractTypes,
handleAddContractType,
newContractType,
setNewContractType,
showAddForms.contractType,
'contractType',
handleAddContractType
)}
{renderTable(
'Service Types',
serviceTypes,
handleAddServiceType,
newServiceType,
setNewServiceType,
showAddForms.serviceType,
'serviceType',
handleAddServiceType
)}
{renderTable(
'Cargo Types',
cargoTypes,
handleAddCargoType,
newCargoType,
setNewCargoType,
showAddForms.cargoType,
'cargoType',
handleAddCargoType
)}
</div>
);
};

View File

@@ -0,0 +1,67 @@
import { useEffect, useState } from "react";
import { api } from "@/auth/http";
const DemoUser1Page = () => {
const [data, setData] = useState<unknown>(null);
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
let cancelled = false;
const run = async () => {
setLoading(true);
setError(null);
try {
const response = await api.get("/test_user1");
if (cancelled) return;
setData(response.data);
} catch (e: any) {
if (cancelled) return;
const message =
e?.response?.data?.message ||
e?.response?.data?.error ||
e?.message ||
"Request failed";
setError(String(message));
} finally {
if (!cancelled) setLoading(false);
}
};
void run();
return () => {
cancelled = true;
};
}, []);
return (
<div className="p-6">
<div className="rounded-2xl border border-border bg-card p-6">
<h1 className="text-lg font-semibold text-foreground">User1 Demo</h1>
<p className="mt-1 text-sm text-muted-foreground">
Calls <code className="font-mono">GET /api/test_user1</code> (requires{' '}
<code className="font-mono">can:demo:user1</code>).
</p>
<div className="mt-4">
{loading ? <p className="text-sm text-muted-foreground">Loading...</p> : null}
{error ? (
<div className="rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">
{error}
</div>
) : null}
{!loading && !error ? (
<pre className="mt-3 overflow-auto rounded-xl border border-border bg-background p-4 text-xs text-foreground">
{JSON.stringify(data, null, 2)}
</pre>
) : null}
</div>
</div>
</div>
);
};
export default DemoUser1Page;

View File

@@ -0,0 +1,67 @@
import { useEffect, useState } from "react";
import { api } from "@/auth/http";
const DemoUser2Page = () => {
const [data, setData] = useState<unknown>(null);
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
let cancelled = false;
const run = async () => {
setLoading(true);
setError(null);
try {
const response = await api.get("/test_user2");
if (cancelled) return;
setData(response.data);
} catch (e: any) {
if (cancelled) return;
const message =
e?.response?.data?.message ||
e?.response?.data?.error ||
e?.message ||
"Request failed";
setError(String(message));
} finally {
if (!cancelled) setLoading(false);
}
};
void run();
return () => {
cancelled = true;
};
}, []);
return (
<div className="p-6">
<div className="rounded-2xl border border-border bg-card p-6">
<h1 className="text-lg font-semibold text-foreground">User2 Demo</h1>
<p className="mt-1 text-sm text-muted-foreground">
Calls <code className="font-mono">GET /api/test_user2</code> (requires{' '}
<code className="font-mono">can:demo:user2</code>).
</p>
<div className="mt-4">
{loading ? <p className="text-sm text-muted-foreground">Loading...</p> : null}
{error ? (
<div className="rounded-xl border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700">
{error}
</div>
) : null}
{!loading && !error ? (
<pre className="mt-3 overflow-auto rounded-xl border border-border bg-background p-4 text-xs text-foreground">
{JSON.stringify(data, null, 2)}
</pre>
) : null}
</div>
</div>
</div>
);
};
export default DemoUser2Page;

View File

@@ -0,0 +1,14 @@
import { ContractTypePage, } from "@/components/ruleEngine/ContractType";
export const RuleEnginePage = () => {
return <div>
<h3>
Rule Engine Page
</h3>
<div>
<ContractTypePage />
</div>
</div>;
};