feat(forklifts): added backend forklift stuff and frontend companies
This commit is contained in:
@@ -159,16 +159,25 @@ function RouteComponent() {
|
||||
updateServer.mutate({ token, field, value: newValue });
|
||||
}
|
||||
};
|
||||
|
||||
let submitting = false;
|
||||
return (
|
||||
<Input
|
||||
value={localValue}
|
||||
onChange={(e) => setLocalValue(e.currentTarget.value)}
|
||||
onBlur={(e) => handleSubmit(e.currentTarget.value.trim())}
|
||||
onBlur={(e) => {
|
||||
if (!submitting) {
|
||||
submitting = true;
|
||||
handleSubmit(e.currentTarget.value.trim());
|
||||
setTimeout(() => (submitting = false), 100); // reset after slight delay
|
||||
}
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
handleSubmit(e.currentTarget.value.trim());
|
||||
e.currentTarget.blur(); // exit edit mode
|
||||
setTimeout(() => (submitting = false), 100);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import axios from "axios";
|
||||
import { toast } from "sonner";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
DialogClose,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { getCompanies } from "@/lib/querys/forklifts/getModules";
|
||||
import { useAppForm } from "../../../../lib/formStuff";
|
||||
|
||||
export default function NewCompanyForm({
|
||||
setOpenDialog,
|
||||
}: {
|
||||
setOpenDialog: any;
|
||||
}) {
|
||||
//const search = useSearch({ from: "/_app/(auth)/login" });
|
||||
const { refetch } = useQuery(getCompanies());
|
||||
|
||||
const form = useAppForm({
|
||||
defaultValues: {
|
||||
name: "",
|
||||
},
|
||||
onSubmit: async ({ value }) => {
|
||||
try {
|
||||
await axios.post("/lst/api/forklifts/companies", {
|
||||
name: value.name,
|
||||
});
|
||||
form.reset();
|
||||
setOpenDialog(false);
|
||||
refetch();
|
||||
toast.success(`${value.name} was just created `);
|
||||
} catch (error) {
|
||||
// @ts-ignore
|
||||
if (!error.response.data.success) {
|
||||
// @ts-ignore
|
||||
toast.error(error?.response?.data.message);
|
||||
} else {
|
||||
// @ts-ignore
|
||||
toast.error(error?.message);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Create New Company</DialogTitle>
|
||||
<DialogDescription>Add the new Leasing company</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
form.handleSubmit();
|
||||
}}
|
||||
>
|
||||
<form.AppField
|
||||
name="name"
|
||||
children={(field) => (
|
||||
<field.InputField
|
||||
label="Company Name"
|
||||
inputType="string"
|
||||
required={true}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</form>
|
||||
<DialogFooter>
|
||||
<DialogClose asChild>
|
||||
<Button variant="outline">Cancel</Button>
|
||||
</DialogClose>
|
||||
<Button type="submit">Submit</Button>
|
||||
</DialogFooter>
|
||||
</>
|
||||
);
|
||||
}
|
||||
282
frontend/src/routes/_app/_forklifts/forklifts/companies.tsx
Normal file
282
frontend/src/routes/_app/_forklifts/forklifts/companies.tsx
Normal file
@@ -0,0 +1,282 @@
|
||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import {
|
||||
createColumnHelper,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
type SortingState,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table";
|
||||
import axios from "axios";
|
||||
import { Activity, ArrowDown, ArrowUp } from "lucide-react";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { getCompanies } from "@/lib/querys/forklifts/getModules";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
type Company = {
|
||||
id: string;
|
||||
name: string;
|
||||
active: boolean;
|
||||
};
|
||||
export const Route = createFileRoute("/_app/_forklifts/forklifts/companies")({
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
const updateCompanyItem = async (
|
||||
id: string,
|
||||
data: Record<string, string | number | boolean | null>,
|
||||
) => {
|
||||
try {
|
||||
const res = await axios.patch(`/lst/api/forklifts/companies/${id}`, data, {
|
||||
withCredentials: true,
|
||||
});
|
||||
toast.success(`Company just updated`);
|
||||
return res;
|
||||
} catch (err) {
|
||||
toast.error("Error in updating company");
|
||||
return err;
|
||||
}
|
||||
};
|
||||
|
||||
function RouteComponent() {
|
||||
const {
|
||||
data: companyData = [],
|
||||
isLoading,
|
||||
refetch,
|
||||
} = useQuery(getCompanies());
|
||||
const [sorting, setSorting] = useState<SortingState>([]);
|
||||
const columnHelper = createColumnHelper<Company>();
|
||||
const submitting = useRef(false);
|
||||
|
||||
const updateCompany = useMutation({
|
||||
mutationFn: ({
|
||||
id,
|
||||
field,
|
||||
value,
|
||||
}: {
|
||||
id: string;
|
||||
field: string;
|
||||
value: string | number | boolean | null;
|
||||
}) => updateCompanyItem(id, { [field]: value }),
|
||||
|
||||
onSuccess: () => {
|
||||
// refetch or update cache
|
||||
refetch();
|
||||
},
|
||||
});
|
||||
const columns = [
|
||||
columnHelper.accessor("name", {
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
>
|
||||
<span className="flex flex-row gap-2">Name</span>
|
||||
{column.getIsSorted() === "asc" ? (
|
||||
<ArrowUp className="ml-2 h-4 w-4" />
|
||||
) : (
|
||||
<ArrowDown className="ml-2 h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
cell: ({ row, getValue }) => {
|
||||
const initialValue = String(getValue() ?? "");
|
||||
const [localValue, setLocalValue] = useState(initialValue);
|
||||
const id = row.original.id;
|
||||
const field = "name";
|
||||
|
||||
useEffect(() => setLocalValue(initialValue), [initialValue]);
|
||||
|
||||
const handleSubmit = (newValue: string) => {
|
||||
if (newValue !== initialValue) {
|
||||
setLocalValue(newValue);
|
||||
updateCompany.mutate({ id, field, value: newValue });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Input
|
||||
value={localValue}
|
||||
onChange={(e) => setLocalValue(e.currentTarget.value)}
|
||||
onBlur={(e) => {
|
||||
if (!submitting.current) {
|
||||
submitting.current = true;
|
||||
handleSubmit(e.currentTarget.value.trim());
|
||||
setTimeout(() => (submitting.current = false), 100); // reset after slight delay
|
||||
}
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
submitting.current = true;
|
||||
handleSubmit(e.currentTarget.value.trim());
|
||||
e.currentTarget.blur(); // will trigger blur, but we ignore it
|
||||
setTimeout(() => (submitting.current = false), 100);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
}),
|
||||
columnHelper.accessor("active", {
|
||||
header: ({ column }) => {
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
>
|
||||
<span className="flex flex-row gap-2">
|
||||
<Activity />
|
||||
Active
|
||||
</span>
|
||||
{column.getIsSorted() === "asc" ? (
|
||||
<ArrowUp className="ml-2 h-4 w-4" />
|
||||
) : (
|
||||
<ArrowDown className="ml-2 h-4 w-4" />
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
},
|
||||
cell: ({ row, getValue }) => {
|
||||
const active = getValue<boolean>();
|
||||
const id = row.original.id;
|
||||
const field = "active";
|
||||
|
||||
return (
|
||||
<Select
|
||||
value={active ? "true" : "false"}
|
||||
onValueChange={(value) => {
|
||||
const newValue = value === "true";
|
||||
updateCompany.mutate({ id, field, value: newValue });
|
||||
}}
|
||||
>
|
||||
<SelectTrigger
|
||||
className={cn(
|
||||
"w-[100px]",
|
||||
active
|
||||
? "border-green-500 text-green-600"
|
||||
: "border-gray-400 text-gray-500",
|
||||
)}
|
||||
>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="true">True</SelectItem>
|
||||
<SelectItem value="false">False</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
);
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
||||
const table = useReactTable({
|
||||
data: companyData,
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
onSortingChange: setSorting,
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
//renderSubComponent: ({ row }: { row: any }) => <ExpandedRow row={row} />,
|
||||
//getRowCanExpand: () => true,
|
||||
state: {
|
||||
sorting,
|
||||
},
|
||||
});
|
||||
if (isLoading) {
|
||||
return <div className="m-auto">Loading user data</div>;
|
||||
}
|
||||
return (
|
||||
<div className="p-4">
|
||||
<div className="w-fit">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
return (
|
||||
<TableHead key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext(),
|
||||
)}
|
||||
</TableHead>
|
||||
);
|
||||
})}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows.map((row) => (
|
||||
<React.Fragment key={row.id}>
|
||||
<TableRow
|
||||
key={row.id}
|
||||
data-state={row.getIsSelected() && "selected"}
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext(),
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
|
||||
{/* {row.getIsExpanded() && (
|
||||
<TableRow>
|
||||
<TableCell colSpan={row.getVisibleCells().length}>
|
||||
{renderSubComponent({ row })}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)} */}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
<div className="flex items-center justify-end space-x-2 py-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => table.previousPage()}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => table.nextPage()}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
9
frontend/src/routes/_app/_forklifts/forklifts/index.tsx
Normal file
9
frontend/src/routes/_app/_forklifts/forklifts/index.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/_app/_forklifts/forklifts/')({
|
||||
component: RouteComponent,
|
||||
})
|
||||
|
||||
function RouteComponent() {
|
||||
return <div>Hello "/_app/_forklifts/"!</div>
|
||||
}
|
||||
19
frontend/src/routes/_app/_forklifts/route.tsx
Normal file
19
frontend/src/routes/_app/_forklifts/route.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import { createFileRoute, Outlet } from "@tanstack/react-router";
|
||||
import { checkUserAccess } from "@/lib/authClient";
|
||||
|
||||
export const Route = createFileRoute("/_app/_forklifts")({
|
||||
beforeLoad: async () =>
|
||||
checkUserAccess({
|
||||
allowedRoles: ["admin", "systemAdmin", "manager", "supervisor"],
|
||||
moduleName: "forklifts", // optional
|
||||
}),
|
||||
component: RouteComponent,
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return (
|
||||
<div>
|
||||
<Outlet />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -14,12 +14,9 @@ import {
|
||||
} from "../../../../../lib/authClient";
|
||||
|
||||
import { AdminSideBar } from "./side-components/admin";
|
||||
import { EomSideBar } from "./side-components/eom";
|
||||
import { ForkliftSideBar } from "./side-components/forklift";
|
||||
import { Header } from "./side-components/header";
|
||||
import { LogisticsSideBar } from "./side-components/logistics";
|
||||
import { ProductionSideBar } from "./side-components/production";
|
||||
import { QualitySideBar } from "./side-components/quality";
|
||||
|
||||
export function AppSidebar() {
|
||||
const { session } = useAuth();
|
||||
@@ -36,9 +33,9 @@ export function AppSidebar() {
|
||||
<LogisticsSideBar user={session?.user as any} userRoles={userRoles} />
|
||||
{userAccess(null, ["systemAdmin"]) && (
|
||||
<>
|
||||
<ForkliftSideBar />
|
||||
{/* <ForkliftSideBar />
|
||||
<EomSideBar />
|
||||
<QualitySideBar />
|
||||
<QualitySideBar /> */}
|
||||
<AdminSideBar />
|
||||
</>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user