feat(frontend): add React UI — procurement office, wizard, suppliers, inventory panel (dash port), role-based login
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
export { useStoreData, useInvalidateStore } from "./useStoreData";
|
||||
export { useQuotes, useCreateQuote, useUpdateQuote, useDeleteQuote } from "./useQuotes";
|
||||
export { useSuppliers, useAddSupplier, useUpdateSupplier, useRemoveSupplier } from "./useSuppliers";
|
||||
export { useMaterials, useAddMaterial } from "./useMaterials";
|
||||
export { useProjects, useAddProject } from "./useProjects";
|
||||
export {
|
||||
useUnits,
|
||||
useSaveRefUnit,
|
||||
useSaveConversion,
|
||||
useDeleteConversion,
|
||||
useSaveStandardConversion,
|
||||
useDeleteStandardConversion,
|
||||
} from "./useUnits";
|
||||
@@ -0,0 +1,23 @@
|
||||
// ============================================================
|
||||
// hooks مواد (کالاها) — دستهی جدید هم یک material است
|
||||
// ============================================================
|
||||
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import type { Material } from "../lib/types";
|
||||
import { api } from "../lib/api";
|
||||
import { useAppStore } from "../store";
|
||||
import { useInvalidateStore } from "./useStoreData";
|
||||
|
||||
export function useMaterials(): Material[] {
|
||||
return useAppStore((s) => s.materials);
|
||||
}
|
||||
|
||||
export function useAddMaterial() {
|
||||
const addMaterial = useAppStore((s) => s.addMaterial);
|
||||
const invalidate = useInvalidateStore();
|
||||
return useMutation({
|
||||
mutationFn: (m: Material) => api.materials.create(m),
|
||||
onMutate: (m) => addMaterial(m),
|
||||
onSettled: () => invalidate(),
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// ============================================================
|
||||
// hooks پروژهها
|
||||
// ============================================================
|
||||
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import type { Project } from "../lib/types";
|
||||
import { api } from "../lib/api";
|
||||
import { useAppStore } from "../store";
|
||||
import { useInvalidateStore } from "./useStoreData";
|
||||
|
||||
export function useProjects(): Project[] {
|
||||
return useAppStore((s) => s.projects);
|
||||
}
|
||||
|
||||
export function useAddProject() {
|
||||
const addProject = useAppStore((s) => s.addProject);
|
||||
const invalidate = useInvalidateStore();
|
||||
return useMutation({
|
||||
mutationFn: (p: Project) => api.projects.create(p),
|
||||
onMutate: (p) => addProject(p),
|
||||
onSettled: () => invalidate(),
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// ============================================================
|
||||
// hooks پیشنهادها — خواندن + mutationهای خوشبینانه
|
||||
// ============================================================
|
||||
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import type { Quote } from "../lib/types";
|
||||
import { api } from "../lib/api";
|
||||
import { useAppStore } from "../store";
|
||||
import { useInvalidateStore } from "./useStoreData";
|
||||
|
||||
export function useQuotes(): Quote[] {
|
||||
return useAppStore((s) => s.quotes);
|
||||
}
|
||||
|
||||
export function useCreateQuote() {
|
||||
const addQuote = useAppStore((s) => s.addQuote);
|
||||
const invalidate = useInvalidateStore();
|
||||
return useMutation({
|
||||
mutationFn: (q: Quote) => api.quotes.create(q),
|
||||
onMutate: (q) => addQuote(q),
|
||||
onSettled: () => invalidate(),
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateQuote() {
|
||||
const updateQuote = useAppStore((s) => s.updateQuote);
|
||||
const invalidate = useInvalidateStore();
|
||||
return useMutation({
|
||||
mutationFn: (args: { id: string; patch: Partial<Quote> }) =>
|
||||
api.quotes.update(args.id, args.patch),
|
||||
onMutate: (args) => updateQuote(args.id, args.patch),
|
||||
onSettled: () => invalidate(),
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteQuote() {
|
||||
const deleteQuote = useAppStore((s) => s.deleteQuote);
|
||||
const invalidate = useInvalidateStore();
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => api.quotes.delete(id),
|
||||
onMutate: (id) => deleteQuote(id),
|
||||
onSettled: () => invalidate(),
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// ============================================================
|
||||
// پایهی داده: یک query واحد که کل store را لود میکند
|
||||
// ============================================================
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { useCallback } from "react";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { api } from "../lib/api";
|
||||
import { useAppStore } from "../store";
|
||||
|
||||
export const STORE_KEY = ["store"] as const;
|
||||
|
||||
// یک بار در ریشهی برنامه صدا زده میشود: لود + mirror در Zustand
|
||||
export function useStoreData(): void {
|
||||
const setData = useAppStore((s) => s.setData);
|
||||
const { data } = useQuery({
|
||||
queryKey: STORE_KEY,
|
||||
queryFn: () => api.listAll(),
|
||||
staleTime: Infinity,
|
||||
});
|
||||
useEffect(() => {
|
||||
if (data) setData(data);
|
||||
}, [data, setData]);
|
||||
}
|
||||
|
||||
// بعد از هر mutation برای همگرایی با حقیقت
|
||||
export function useInvalidateStore(): () => void {
|
||||
const qc = useQueryClient();
|
||||
return useCallback(() => {
|
||||
void qc.invalidateQueries({ queryKey: STORE_KEY });
|
||||
}, [qc]);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// ============================================================
|
||||
// hooks تأمینکنندهها و دستهها
|
||||
// ============================================================
|
||||
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import type { Supplier } from "../lib/types";
|
||||
import { api } from "../lib/api";
|
||||
import { useAppStore } from "../store";
|
||||
import { useInvalidateStore } from "./useStoreData";
|
||||
|
||||
export function useSuppliers(): Supplier[] {
|
||||
return useAppStore((s) => s.suppliers);
|
||||
}
|
||||
|
||||
export function useAddSupplier() {
|
||||
const addSupplier = useAppStore((s) => s.addSupplier);
|
||||
const invalidate = useInvalidateStore();
|
||||
return useMutation({
|
||||
mutationFn: (s: Supplier) => api.suppliers.create(s),
|
||||
onMutate: (s) => addSupplier(s),
|
||||
onSettled: () => invalidate(),
|
||||
});
|
||||
}
|
||||
|
||||
export function useUpdateSupplier() {
|
||||
const updateSupplier = useAppStore((s) => s.updateSupplier);
|
||||
const invalidate = useInvalidateStore();
|
||||
return useMutation({
|
||||
mutationFn: (args: { id: string; patch: Partial<Supplier> }) => api.suppliers.update(args.id, args.patch),
|
||||
onMutate: (args) => updateSupplier(args.id, args.patch),
|
||||
onSettled: () => invalidate(),
|
||||
});
|
||||
}
|
||||
|
||||
export function useRemoveSupplier() {
|
||||
const deleteSupplier = useAppStore((s) => s.deleteSupplier);
|
||||
const invalidate = useInvalidateStore();
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => api.suppliers.delete(id),
|
||||
onMutate: (id) => deleteSupplier(id),
|
||||
onSettled: () => invalidate(),
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
// ============================================================
|
||||
// hooks واحدها — واحد مرجع، ضرایب دستهای و ثابت
|
||||
// ============================================================
|
||||
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import type { CategoryUnit, StandardConversion, UnitConversion } from "../lib/types";
|
||||
import { api } from "../lib/api";
|
||||
import { useAppStore } from "../store";
|
||||
import { useInvalidateStore } from "./useStoreData";
|
||||
|
||||
export function useUnits(): {
|
||||
categoryUnits: CategoryUnit[];
|
||||
unitConversions: UnitConversion[];
|
||||
standardConversions: StandardConversion[];
|
||||
} {
|
||||
const categoryUnits = useAppStore((s) => s.categoryUnits);
|
||||
const unitConversions = useAppStore((s) => s.unitConversions);
|
||||
const standardConversions = useAppStore((s) => s.standardConversions);
|
||||
return { categoryUnits, unitConversions, standardConversions };
|
||||
}
|
||||
|
||||
export function useSaveRefUnit() {
|
||||
const setCategoryUnit = useAppStore((s) => s.setCategoryUnit);
|
||||
const invalidate = useInvalidateStore();
|
||||
return useMutation({
|
||||
mutationFn: (cu: CategoryUnit) => api.units.saveCategoryUnit(cu),
|
||||
onMutate: (cu) => setCategoryUnit(cu),
|
||||
onSettled: () => invalidate(),
|
||||
});
|
||||
}
|
||||
|
||||
export function useSaveConversion() {
|
||||
const setConversion = useAppStore((s) => s.setConversion);
|
||||
const invalidate = useInvalidateStore();
|
||||
return useMutation({
|
||||
mutationFn: (c: UnitConversion) => api.units.saveConversion(c),
|
||||
onMutate: (c) => setConversion(c),
|
||||
onSettled: () => invalidate(),
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteConversion() {
|
||||
const deleteConversion = useAppStore((s) => s.deleteConversion);
|
||||
const invalidate = useInvalidateStore();
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => api.units.deleteConversion(id),
|
||||
onMutate: (id) => deleteConversion(id),
|
||||
onSettled: () => invalidate(),
|
||||
});
|
||||
}
|
||||
|
||||
export function useSaveStandardConversion() {
|
||||
const setStandardConversion = useAppStore((s) => s.setStandardConversion);
|
||||
const invalidate = useInvalidateStore();
|
||||
return useMutation({
|
||||
mutationFn: (sc: StandardConversion) => api.units.saveStandardConversion(sc),
|
||||
onMutate: (sc) => setStandardConversion(sc),
|
||||
onSettled: () => invalidate(),
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteStandardConversion() {
|
||||
const deleteStandardConversion = useAppStore((s) => s.deleteStandardConversion);
|
||||
const invalidate = useInvalidateStore();
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => api.units.deleteStandardConversion(id),
|
||||
onMutate: (id) => deleteStandardConversion(id),
|
||||
onSettled: () => invalidate(),
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user