Explorar el Código

adding access right, daily mat forecast

master
padre
commit
fdad10a362
Se han modificado 5 ficheros con 121 adiciones y 73 borrados
  1. +2
    -0
      src/app/api/scheduling/index.ts
  2. +7
    -0
      src/authorities.ts
  3. +0
    -6
      src/authorties.ts
  4. +41
    -41
      src/components/NavigationContent/NavigationContent.tsx
  5. +71
    -26
      src/config/authConfig.ts

+ 2
- 0
src/app/api/scheduling/index.ts Ver fichero

@@ -9,6 +9,7 @@ export type ScheduleType = "all" | "rough" | "detailed" | "manual";
export interface RoughProdScheduleResult {
id: number;
scheduleAt: number[];
produceAt: number[];
schedulePeriod: number[];
schedulePeriodTo: number[];
totalEstProdCount: number;
@@ -80,6 +81,7 @@ export interface RoughProdScheduleLineResultByBomByDate {
// Detailed
export interface DetailedProdScheduleResult {
id: number;
produceAt: number[];
scheduleAt: number[];
totalEstProdCount: number;
totalFGType: number;


+ 7
- 0
src/authorities.ts Ver fichero

@@ -0,0 +1,7 @@
export const [VIEW_USER, VIEW_DO, MAINTAIN_USER, VIEW_GROUP, MAINTAIN_GROUP] = [
"VIEW_USER",
"VIEW_DO",
"MAINTAIN_USER",
"VIEW_GROUP",
"MAINTAIN_GROUP",
];

+ 0
- 6
src/authorties.ts Ver fichero

@@ -1,6 +0,0 @@
export const [VIEW_USER, MAINTAIN_USER, VIEW_GROUP, MAINTAIN_GROUP] = [
"VIEW_USER",
"MAINTAIN_USER",
"VIEW_GROUP",
"MAINTAIN_GROUP",
];

+ 41
- 41
src/components/NavigationContent/NavigationContent.tsx Ver fichero

@@ -1,3 +1,4 @@
import { useSession } from "next-auth/react";
import Divider from "@mui/material/Divider";
import Box from "@mui/material/Box";
import React, { useEffect } from "react";
@@ -24,6 +25,15 @@ import { usePathname } from "next/navigation";
import Link from "next/link";
import { NAVIGATION_CONTENT_WIDTH } from "@/config/uiConfig";
import Logo from "../Logo";
import {
VIEW_USER,
VIEW_DO,
MAINTAIN_USER,
VIEW_GROUP,
MAINTAIN_GROUP,
// Add more authorities as needed, e.g.:
// VIEW_PO, MAINTAIN_PO, VIEW_INVENTORY, etc.
} from "../../authorities";

interface NavigationItem {
icon: React.ReactNode;
@@ -31,9 +41,22 @@ interface NavigationItem {
path: string;
children?: NavigationItem[];
isHidden?: true | undefined;
requiredAbility?: string | string[];
}

const NavigationContent: React.FC = () => {
const { data: session, status } = useSession();
const abilities = session?.user?.abilities ?? [];

// Helper: check if user has required permission
const hasAbility = (required?: string | string[]): boolean => {
if (!required) return true; // no requirement → always show
if (Array.isArray(required)) {
return required.some(ability => abilities.includes(ability));
}
return abilities.includes(required);
};

const navigationItems: NavigationItem[] = [
{
icon: <Dashboard />,
@@ -108,49 +131,12 @@ const NavigationContent: React.FC = () => {
},
],
},
// {
// icon: <RequestQuote />,
// label: "Production",
// path: "",
// children: [
// {
// icon: <RequestQuote />,
// label: "Job Order",
// path: "",
// },
// {
// icon: <RequestQuote />,
// label: "Job Order Traceablity ",
// path: "",
// },
// {
// icon: <RequestQuote />,
// label: "Work Order",
// path: "",
// },
// {
// icon: <RequestQuote />,
// label: "Work Order Traceablity ",
// path: "",
// },
// ],
// },
// {
// icon: <RequestQuote />,
// label: "Quality Control Log",
// path: "",
// children: [
// {
// icon: <RequestQuote />,
// label: "Quality Control Log",
// path: "",
// },
// ],
// },
{
icon: <RequestQuote />,
label: "Delivery",
path: "",
//requiredAbility: VIEW_DO,
requiredAbility: VIEW_USER,
children: [
{
icon: <RequestQuote />,
@@ -248,16 +234,19 @@ const NavigationContent: React.FC = () => {
icon: <RequestQuote />,
label: "Settings",
path: "",
requiredAbility: [VIEW_USER, VIEW_GROUP],
children: [
{
icon: <RequestQuote />,
label: "User",
path: "/settings/user",
requiredAbility: VIEW_USER,
},
{
icon: <RequestQuote />,
label: "User Group",
path: "/settings/user",
requiredAbility: VIEW_GROUP,
},
// {
// icon: <RequestQuote />,
@@ -360,7 +349,12 @@ const NavigationContent: React.FC = () => {
};

const renderNavigationItem = (item: NavigationItem) => {
if (!hasAbility(item.requiredAbility)) {
return null;
}

const isOpen = openItems.includes(item.label);
const hasVisibleChildren = item.children?.some(child => hasAbility(child.requiredAbility));

return (
<Box
@@ -376,7 +370,7 @@ const NavigationContent: React.FC = () => {
<ListItemIcon>{item.icon}</ListItemIcon>
<ListItemText primary={t(item.label)} />
</ListItemButton>
{item.children && isOpen && (
{item.children && isOpen && hasVisibleChildren && (
<List sx={{ pl: 2 }}>
{item.children.map(
(child) => !child.isHidden && renderNavigationItem(child),
@@ -387,6 +381,10 @@ const NavigationContent: React.FC = () => {
);
};

if (status === "loading") {
return <Box sx={{ width: NAVIGATION_CONTENT_WIDTH, p: 3 }}>Loading...</Box>;
}

return (
<Box sx={{ width: NAVIGATION_CONTENT_WIDTH }}>
<Box sx={{ p: 3, display: "flex" }}>
@@ -397,7 +395,9 @@ const NavigationContent: React.FC = () => {
</Box>
<Divider />
<List component="nav">
{navigationItems.map((item) => renderNavigationItem(item))}
{navigationItems
.map(renderNavigationItem)
.filter(Boolean)}
{/* {navigationItems.map(({ icon, label, path }, index) => {
return (
<Box


+ 71
- 26
src/config/authConfig.ts Ver fichero

@@ -1,12 +1,38 @@
import { AuthOptions, Session } from "next-auth";
// config/authConfig.ts (or wherever your authOptions live)
import { AuthOptions } from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import { LOGIN_API_PATH } from "./api";

export interface SessionWithTokens extends Session {
accessToken: string | null;
refreshToken?: string;
abilities: string[];
id?: string | null;
// Extend the built-in types
declare module "next-auth" {
interface Session {
accessToken: string | null;
refreshToken?: string;
abilities: string[];
id?: string;
user: {
name?: string | null;
email?: string | null;
image?: string | null;
abilities: string[]; // add abilities to user object too if you need it client-side
};
}

interface User {
id?: string;
accessToken: string | null;
refreshToken?: string;
abilities: string[];
}
}

declare module "next-auth/jwt" {
interface JWT {
id?: string;
accessToken: string | null;
refreshToken?: string;
abilities: string[];
}
}

export const authOptions: AuthOptions = {
@@ -19,18 +45,26 @@ export const authOptions: AuthOptions = {
username: { label: "Username", type: "text" },
password: { label: "Password", type: "password" },
},
async authorize(credentials, req) {
async authorize(credentials) {
if (!credentials?.username || !credentials?.password) return null;

const res = await fetch(LOGIN_API_PATH, {
method: "POST",
body: JSON.stringify(credentials),
headers: { "Content-Type": "application/json" },
});

if (!res.ok) return null;

const user = await res.json();

if (res.ok && user) {
return user;
// Important: next-auth expects the user object returned here
// to be serializable and contain the fields you want in token/session
// Ensure your backend returns: { id, accessToken, abilities, ...other fields }
if (user && user.abilities) {
return user; // this will be passed to jwt callback as `user`
}

return null;
},
}),
@@ -39,25 +73,36 @@ export const authOptions: AuthOptions = {
signIn: "/login",
},
callbacks: {
jwt(params) {
// Add the data from user to the token
const { token, user } = params;
const newToken = { ...token, ...user };
return newToken;
// Persist custom fields into the JWT token
async jwt({ token, user }) {
// First sign-in: `user` is available
if (user) {
token.id = user.id ?? token.sub; // fallback to sub if no id
token.accessToken = user.accessToken;
token.refreshToken = user.refreshToken;
token.abilities = user.abilities ?? [];
}

// On subsequent calls (token refresh, session access), user is not present
// so we just return the existing token with custom fields preserved
return token;
},
session({ session, token }) {
const sessionWithToken: SessionWithTokens = {
...session,
// Add the data from the token to the session
id: token.id as string | undefined,
accessToken: token.accessToken as string | null,
refreshToken: token.refreshToken as string | undefined,
abilities: token.abilities as string[],
};
if (sessionWithToken.user) {
sessionWithToken.user.abilities = token.abilities as string[];
// Expose custom fields to the client session
async session({ session, token }) {
session.id = token.id as string | undefined;
session.accessToken = token.accessToken as string | null;
session.refreshToken = token.refreshToken as string | undefined;
session.abilities = token.abilities as string[];
// Also add abilities to session.user for easier client-side access
if (session.user) {
session.user.abilities = token.abilities as string[];
}
return sessionWithToken;

return session;
},
},
};

export default authOptions;

Cargando…
Cancelar
Guardar