feat(erp-pm): integrate unified access matrix, streamline limited procurement flow, and brand as integrated project management, procurement and warehouse platform
This commit is contained in:
@@ -213,7 +213,11 @@ export function Step4Projects({ items, projects, assignment, onAssign, onUnassig
|
||||
/>
|
||||
))}
|
||||
{!projects.length && (
|
||||
<div className="dd-empty">هنوز پروژهای تعریف نشده است — از پایین یک پروژه اضافه کنید.</div>
|
||||
<div className="dd-empty">
|
||||
{canAddProject
|
||||
? "هنوز پروژهای تعریف نشده است — از پایین یک پروژه اضافه کنید."
|
||||
: "هنوز پروژهای به شما تخصیص داده نشده است — با مدیر سیستم تماس بگیرید."}
|
||||
</div>
|
||||
)}
|
||||
{/* افزودن پروژه بهصورت درجا — زیر لیست پروژهها (سطح limited: ممنوع) */}
|
||||
<div className="mt-3 rounded-md border border-dashed border-line p-2" hidden={!canAddProject}>
|
||||
|
||||
@@ -22,6 +22,7 @@ import { normalize } from "../../lib/normalize";
|
||||
import { faNum } from "../../lib/formatters";
|
||||
import { matchMaterial } from "../../lib/quoteUtils";
|
||||
import { Header, PageLayout } from "../../components/layout/Header";
|
||||
import { AppSwitcherMenu } from "../../components/AppSwitcherMenu";
|
||||
import { Stepper } from "./Stepper";
|
||||
import { Step1TopicSupplier } from "./Step1TopicSupplier";
|
||||
import { Step2LineItems, type ItemDraft, type MaterialRec } from "./Step2LineItems";
|
||||
@@ -54,13 +55,19 @@ export function WizardPage() {
|
||||
const user = useAuthStore((s) => s.user);
|
||||
const isLimitedProcurement =
|
||||
user?.panels?.procurement === "limited" && user?.role !== "admin";
|
||||
const memberProjectNames = useMemo(
|
||||
() =>
|
||||
projects
|
||||
.filter((p) => (user?.member_project_ids ?? []).includes(p.id))
|
||||
.map((p) => p.name),
|
||||
[projects, user],
|
||||
);
|
||||
const memberProjectNames = useMemo(() => {
|
||||
const allowedIds = new Set([
|
||||
...(user?.member_project_ids ?? []),
|
||||
...(user?.projects ?? []).map((p) => p.id),
|
||||
...(user?.project_id ? [user.project_id] : []),
|
||||
]);
|
||||
const matched = projects
|
||||
.filter((p) => allowedIds.has(p.id))
|
||||
.map((p) => p.name);
|
||||
const directNames = (user?.projects ?? []).map((p) => p.name);
|
||||
if (user?.project_name) directNames.push(user.project_name);
|
||||
return [...new Set([...matched, ...directNames])];
|
||||
}, [projects, user]);
|
||||
const topics = useAppStore((s) => s.topics);
|
||||
const createQuote = useCreateQuote();
|
||||
const addSupplier = useAddSupplier();
|
||||
@@ -95,22 +102,37 @@ export function WizardPage() {
|
||||
const [emptyFields, setEmptyFields] = useState<{ label: string; step: number }[]>([]);
|
||||
const [confirmOpen, setConfirmOpen] = useState(false);
|
||||
|
||||
// ---------- prefill از لینکها (?topic=&supplier=) ----------
|
||||
// ---------- prefill از لینکها (?topic=&supplier=&project=) ----------
|
||||
useEffect(() => {
|
||||
const t = params.get("topic");
|
||||
if (!t) return;
|
||||
const topic = topicByName(t, topics);
|
||||
if (!topic) return;
|
||||
setSelectedTopic(topic.name);
|
||||
const sid = params.get("supplier");
|
||||
if (sid) {
|
||||
const sup = suppliers.find((s) => s.id === sid);
|
||||
if (sup && normalize(sup.topic) === normalize(topic.name)) {
|
||||
setSupplierName(sup.name);
|
||||
setSupplierInvalid(false);
|
||||
if (t) {
|
||||
const topic = topicByName(t, topics);
|
||||
if (topic) {
|
||||
setSelectedTopic(topic.name);
|
||||
const sid = params.get("supplier");
|
||||
if (sid) {
|
||||
const sup = suppliers.find((s) => s.id === sid);
|
||||
if (sup && normalize(sup.topic) === normalize(topic.name)) {
|
||||
setSupplierName(sup.name);
|
||||
setSupplierInvalid(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [params, suppliers, topics]);
|
||||
|
||||
const proj = params.get("project");
|
||||
if (proj) {
|
||||
setAssignment((cur) => {
|
||||
const next = { ...cur };
|
||||
for (const it of items) {
|
||||
if (!next[it.tempId]?.length) {
|
||||
next[it.tempId] = [proj];
|
||||
}
|
||||
}
|
||||
return next;
|
||||
});
|
||||
}
|
||||
}, [params, suppliers, topics, items]);
|
||||
|
||||
// ---------- اعتبارسنجی قبل از ترک هر مرحله ----------
|
||||
const completedSteps = useMemo(() => {
|
||||
@@ -143,11 +165,10 @@ export function WizardPage() {
|
||||
|
||||
// ---------- پروژهها ----------
|
||||
const projectNames = useMemo(() => {
|
||||
const names = new Set<string>(
|
||||
isLimitedProcurement
|
||||
? memberProjectNames
|
||||
: projects.map((p) => p.name),
|
||||
);
|
||||
if (isLimitedProcurement) {
|
||||
return memberProjectNames;
|
||||
}
|
||||
const names = new Set<string>(projects.map((p) => p.name));
|
||||
quotes.forEach((q) => {
|
||||
if (q.project) names.add(q.project);
|
||||
(q.projects || []).forEach((n) => names.add(n));
|
||||
@@ -155,6 +176,24 @@ export function WizardPage() {
|
||||
return [...names];
|
||||
}, [projects, quotes, isLimitedProcurement, memberProjectNames]);
|
||||
|
||||
// سطح محدود: در صورت داشتن یک پروژه، خودکار اقلام به آن پروژه تخصیص مییابند
|
||||
useEffect(() => {
|
||||
if (isLimitedProcurement && memberProjectNames.length === 1) {
|
||||
const sole = memberProjectNames[0];
|
||||
setAssignment((cur) => {
|
||||
let changed = false;
|
||||
const next = { ...cur };
|
||||
for (const it of items) {
|
||||
if (!next[it.tempId]?.length) {
|
||||
next[it.tempId] = [sole];
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed ? next : cur;
|
||||
});
|
||||
}
|
||||
}, [isLimitedProcurement, memberProjectNames, items]);
|
||||
|
||||
const assign = (tempId: string, proj: string) => {
|
||||
setAssignment((a) => {
|
||||
const cur = a[tempId] || [];
|
||||
@@ -431,8 +470,9 @@ export function WizardPage() {
|
||||
<Header
|
||||
title="افزودن پیشنهاد جدید"
|
||||
sub="اطلاعات را از تماس با تأمینکننده وارد کنید"
|
||||
backTo="/"
|
||||
backLabel="بازگشت به دفتر"
|
||||
backTo={isLimitedProcurement ? "/requester" : "/"}
|
||||
backLabel={isLimitedProcurement ? "بازگشت به استعلامها" : "بازگشت به دفتر"}
|
||||
menu={<AppSwitcherMenu activeModule="procurement" />}
|
||||
/>
|
||||
<PageLayout>
|
||||
{/* نوار مراحل استپر */}
|
||||
|
||||
Reference in New Issue
Block a user