고객사 등록 완료
This commit is contained in:
parent
241c1f94e4
commit
5f778d9a2b
114
plm-api/src/controllers/app/common/oemmng.controller.js
Normal file
114
plm-api/src/controllers/app/common/oemmng.controller.js
Normal file
@ -0,0 +1,114 @@
|
||||
// src/controllers/admin/users/users.controller.js
|
||||
const express = require("express");
|
||||
const router = express.Router();
|
||||
const oemMngService = require("../../../services/oemmng.service");
|
||||
const authMiddleware = require("../../../middleware/auth.middleware");
|
||||
const roleCheck = require("../../../middleware/roleCheck.middleware");
|
||||
const { body, param } = require("express-validator");
|
||||
const validate = require("../../../middleware/validator.middleware");
|
||||
|
||||
router.use(authMiddleware);
|
||||
router.use(roleCheck(["super_admin", "company_admin"]));
|
||||
|
||||
// 고객사 목록 조회
|
||||
router.get("/oemmngList", async (req, res, next) => {
|
||||
try {
|
||||
const page = parseInt(req.query.page) || 1;
|
||||
const limit = parseInt(req.query.limit) || 10;
|
||||
|
||||
const result = await oemMngService.findAll(req.user, page, limit);
|
||||
res.json(result);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
// Create OemMng
|
||||
router.post(
|
||||
"/oemmngCreate",
|
||||
[
|
||||
body("oem_code").notEmpty().withMessage("OEM 코드가 필요합니다"),
|
||||
body("oem_name").notEmpty().withMessage("OEM 이름이 필요합니다"),
|
||||
body("companyId").isUUID().withMessage("유효한 회사 ID가 필요합니다"),
|
||||
validate,
|
||||
],
|
||||
async (req, res, next) => {
|
||||
try {
|
||||
const oemMng = await oemMngService.createOemMng(req.body, req.user);
|
||||
res.status(201).json(oemMng);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Update OemMng
|
||||
router.put(
|
||||
"/oemmngUpdate/:id",
|
||||
[
|
||||
param("id").isUUID().withMessage("유효한 ID가 필요합니다"),
|
||||
body("oem_code").optional().notEmpty().withMessage("OEM 코드가 필요합니다"),
|
||||
body("oem_name").optional().notEmpty().withMessage("OEM 이름이 필요합니다"),
|
||||
body("companyId").optional().isUUID().withMessage("유효한 회사 ID가 필요합니다"),
|
||||
validate,
|
||||
],
|
||||
async (req, res, next) => {
|
||||
try {
|
||||
const oemMng = await oemMngService.findById(req.params.id);
|
||||
|
||||
if (!oemMng) {
|
||||
return res.status(404).json({ message: "고객사를 찾을 수 없습니다" });
|
||||
}
|
||||
|
||||
// company_admin은 자신의 회사 고객사만 수정 가능
|
||||
if (
|
||||
req.user.role === "company_admin" &&
|
||||
oemMng.companyId !== req.user.companyId
|
||||
) {
|
||||
return res.status(403).json({
|
||||
message: "다른 회사의 고객사를 수정할 수 없습니다",
|
||||
});
|
||||
}
|
||||
|
||||
const updatedOemMng = await oemMngService.updateOemMng(req.params.id, req.body, req.user);
|
||||
res.json(updatedOemMng);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Delete OemMng
|
||||
router.delete(
|
||||
"/oemmngDelete/:id",
|
||||
[
|
||||
param("id").isUUID().withMessage("유효한 ID가 필요합니다"),
|
||||
validate,
|
||||
],
|
||||
async (req, res, next) => {
|
||||
try {
|
||||
const oemMng = await oemMngService.findById(req.params.id);
|
||||
|
||||
if (!oemMng) {
|
||||
return res.status(404).json({ message: "고객사를 찾을 수 없습니다" });
|
||||
}
|
||||
|
||||
// company_admin은 자신의 회사 고객사만 삭제 가능
|
||||
if (
|
||||
req.user.role === "company_admin" &&
|
||||
oemMng.companyId !== req.user.companyId
|
||||
) {
|
||||
return res.status(403).json({
|
||||
message: "다른 회사의 고객사를 삭제할 수 없습니다",
|
||||
});
|
||||
}
|
||||
|
||||
await oemMngService.deleteOemMng(req.params.id, req.user);
|
||||
res.status(204).end();
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
module.exports = router;
|
@ -67,6 +67,7 @@ class Company extends Model {
|
||||
this.hasMany(models.Equipment, { foreignKey: "companyId" });
|
||||
this.hasMany(models.MaintenanceLog, { foreignKey: "companyId" });
|
||||
this.hasMany(models.ApiKey, { foreignKey: "companyId" });
|
||||
this.hasMany(models.OemMng, { foreignKey: "companyId" });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,14 +27,19 @@ class OemMng extends Model {
|
||||
type: DataTypes.DATE,
|
||||
allowNull: true,
|
||||
},
|
||||
status: {
|
||||
type: DataTypes.STRING(32),
|
||||
allowNull: true,
|
||||
isActive: {
|
||||
type: DataTypes.BOOLEAN,
|
||||
defaultValue: true,
|
||||
},
|
||||
oem_no: {
|
||||
type: DataTypes.STRING(32),
|
||||
allowNull: true,
|
||||
},
|
||||
// 새로 추가할 필드들
|
||||
companyId: {
|
||||
type: DataTypes.UUID,
|
||||
comment: "회사 ID",
|
||||
},
|
||||
},
|
||||
{
|
||||
sequelize,
|
||||
@ -50,6 +55,12 @@ class OemMng extends Model {
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
static associate(models) {
|
||||
// OemMng이 Company에 속함
|
||||
this.belongsTo(models.Company, { foreignKey: "companyId" });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = OemMng;
|
@ -17,6 +17,7 @@ const healthController = require("../controllers/app/health/health.controller");
|
||||
const companiesController = require("../controllers/admin/companies/companies.controller");
|
||||
const deviceController = require("../controllers/app/device/device.controller");
|
||||
const commonController = require("../controllers/app/common/common.controller");
|
||||
const oemMngController = require("../controllers/app/common/oemmng.controller");
|
||||
|
||||
router.use("/health", healthController);
|
||||
router.use("/auth", authController);
|
||||
@ -33,5 +34,6 @@ router.use("/department", departmentController);
|
||||
router.use("/companies", companiesController);
|
||||
router.use("/devices", deviceController);
|
||||
router.use("/common", commonController);
|
||||
router.use("/oemmng", oemMngController);
|
||||
|
||||
module.exports = router;
|
||||
|
@ -1,53 +1,36 @@
|
||||
// src/services/oemmng.service.js
|
||||
const {
|
||||
User,
|
||||
OemMng,
|
||||
Company,
|
||||
Branch,
|
||||
Department,
|
||||
UserRole,
|
||||
Role,
|
||||
} = require("../models");
|
||||
const { Op } = require("sequelize");
|
||||
//const { Op } = require("sequelize");
|
||||
const alertService = require("./alert.service");
|
||||
|
||||
class OemMngService {
|
||||
async findAll(currentUser, page = 1, limit = 10) {
|
||||
try {
|
||||
// Initialize the where clause
|
||||
let where = {
|
||||
role: { [Op.ne]: "super_admin" }, // Exclude super_admin users
|
||||
};
|
||||
let where = {};
|
||||
|
||||
// company_admin은 자신의 회사 유저만 조회 가능
|
||||
if (currentUser.role === "company_admin") {
|
||||
where.companyId = currentUser.companyId;
|
||||
}
|
||||
// isActive 필터 추가
|
||||
//where.isActive = { [Op.eq]: true };
|
||||
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
// 전체 개수 조회
|
||||
const count = await User.count({ where });
|
||||
const count = await OemMng.count({ where });
|
||||
|
||||
const users = await User.findAll({
|
||||
const oemMngs = await OemMng.findAll({
|
||||
where,
|
||||
include: [
|
||||
{
|
||||
model: Company,
|
||||
attributes: ["id", "name"],
|
||||
},
|
||||
{
|
||||
model: Branch,
|
||||
attributes: ["id", "name"],
|
||||
},
|
||||
{
|
||||
model: Department,
|
||||
attributes: ["id", "name"],
|
||||
},
|
||||
{
|
||||
model: Role,
|
||||
through: { attributes: [] },
|
||||
attributes: ["id", "name", "description"],
|
||||
},
|
||||
],
|
||||
order: [["createdAt", "DESC"]],
|
||||
offset,
|
||||
@ -56,17 +39,17 @@ class OemMngService {
|
||||
});
|
||||
|
||||
// 인덱스 추가
|
||||
const usersWithIndex = users.map((user, index) => {
|
||||
const userJson = user.toJSON();
|
||||
userJson.index = offset + index + 1;
|
||||
return userJson;
|
||||
const oemMngsWithIndex = oemMngs.map((oem, index) => {
|
||||
const oemJson = oem.toJSON();
|
||||
oemJson.index = offset + index + 1;
|
||||
return oemJson;
|
||||
});
|
||||
|
||||
return {
|
||||
total: count,
|
||||
totalPages: Math.ceil(count / limit),
|
||||
currentPage: page,
|
||||
users: usersWithIndex,
|
||||
oemMngs: oemMngsWithIndex,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error in findAll:", error);
|
||||
@ -75,34 +58,26 @@ class OemMngService {
|
||||
}
|
||||
|
||||
async findById(id) {
|
||||
return await User.findByPk(id, {
|
||||
return await OemMng.findByPk(id, {
|
||||
include: [
|
||||
{
|
||||
model: Company,
|
||||
attributes: ["id", "name"],
|
||||
},
|
||||
{
|
||||
model: Branch,
|
||||
attributes: ["id", "name"],
|
||||
},
|
||||
{
|
||||
model: Department,
|
||||
attributes: ["id", "name"],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
async createUser(userData, currentUser) {
|
||||
const { roleId, ...userFields } = userData;
|
||||
async createOemMng(oemMngData, currentUser) {
|
||||
const { roleId, ...oemMngFields } = oemMngData;
|
||||
|
||||
const user = await User.create(userFields);
|
||||
const oemMng = await OemMng.create(oemMngFields);
|
||||
|
||||
if (roleId) {
|
||||
const role = await Role.findOne({
|
||||
where: {
|
||||
id: roleId,
|
||||
companyId: user.companyId,
|
||||
companyId: oemMng.companyId,
|
||||
},
|
||||
});
|
||||
|
||||
@ -110,26 +85,23 @@ class OemMngService {
|
||||
throw new Error("Role not found or does not belong to the company");
|
||||
}
|
||||
|
||||
await UserRole.create({
|
||||
userId: user.id,
|
||||
roleId: role.id,
|
||||
});
|
||||
await oemMng.addRole(role);
|
||||
}
|
||||
|
||||
await alertService.createAlert({
|
||||
type: "info",
|
||||
message: `유저 ${user.name}이(가) ${currentUser.name}에 의해 생성되었습니다.`,
|
||||
companyId: user.companyId,
|
||||
message: `고객사 ${oemMng.name}이(가) ${currentUser.name}에 의해 생성되었습니다.`,
|
||||
companyId: oemMng.companyId,
|
||||
});
|
||||
|
||||
return user;
|
||||
return oemMng;
|
||||
}
|
||||
|
||||
async updateUser(id, updateData, currentUser) {
|
||||
const { roleId, ...userFields } = updateData;
|
||||
async updateOemMng(id, updateData, currentUser) {
|
||||
const { roleId, ...oemMngFields } = updateData;
|
||||
|
||||
const user = await User.findByPk(id);
|
||||
if (!user) throw new Error("User not found");
|
||||
const oemMng = await OemMng.findByPk(id);
|
||||
if (!oemMng) throw new Error("OemMng not found");
|
||||
|
||||
// company_admin은 특정 필드를 수정할 수 없음 (예: role을 super_admin으로 변경 불가)
|
||||
if (currentUser.role === "company_admin") {
|
||||
@ -139,13 +111,13 @@ class OemMngService {
|
||||
updateData.companyId = currentUser.companyId;
|
||||
}
|
||||
|
||||
const updatedUser = await user.update(userFields);
|
||||
const updatedOemMng = await oemMng.update(oemMngFields);
|
||||
|
||||
if (roleId) {
|
||||
const role = await Role.findOne({
|
||||
where: {
|
||||
id: roleId,
|
||||
companyId: user.companyId,
|
||||
companyId: oemMng.companyId,
|
||||
},
|
||||
});
|
||||
|
||||
@ -153,33 +125,30 @@ class OemMngService {
|
||||
throw new Error("Role not found or does not belong to the company");
|
||||
}
|
||||
|
||||
await UserRole.upsert({
|
||||
userId: user.id,
|
||||
roleId: role.id,
|
||||
});
|
||||
await oemMng.addRole(role);
|
||||
}
|
||||
|
||||
await alertService.createAlert({
|
||||
type: "info",
|
||||
message: `유저 ${user.name}이(가) ${currentUser.name}에 의해 수정되었습니다.`,
|
||||
companyId: user.companyId,
|
||||
message: `고객사 ${oemMng.name}이(가) ${currentUser.name}에 의해 수정되었습니다.`,
|
||||
companyId: oemMng.companyId,
|
||||
});
|
||||
|
||||
return updatedUser;
|
||||
return updatedOemMng;
|
||||
}
|
||||
|
||||
async deleteUser(id, currentUser) {
|
||||
const user = await User.findByPk(id);
|
||||
if (!user) throw new Error("User not found");
|
||||
async deleteOemMng(id, currentUser) {
|
||||
const oemMng = await OemMng.findByPk(id);
|
||||
if (!oemMng) throw new Error("OemMng not found");
|
||||
|
||||
const userName = user.name;
|
||||
const companyId = user.companyId;
|
||||
const oemMngName = oemMng.name;
|
||||
const companyId = oemMng.companyId;
|
||||
|
||||
await user.destroy();
|
||||
await oemMng.destroy();
|
||||
|
||||
await alertService.createAlert({
|
||||
type: "info",
|
||||
message: `유저 ${userName}이(가) ${currentUser.name}에 의해 삭제되었습니다.`,
|
||||
message: `고객사 ${oemMngName}이(가) ${currentUser.name}에 의해 삭제되었습니다.`,
|
||||
companyId: companyId,
|
||||
});
|
||||
|
||||
@ -187,4 +156,4 @@ class OemMngService {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new UserService();
|
||||
module.exports = new OemMngService();
|
@ -42,7 +42,7 @@ type FormSchema = z.infer<typeof formSchema>;
|
||||
|
||||
interface OemFormProps {
|
||||
initialData?: Partial<oemMng>;
|
||||
onSubmit: (data: Partial<User>) => void;
|
||||
onSubmit: (data: Partial<oemMng>) => void;
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
@ -51,7 +51,7 @@ export const OemMngForm: React.FC<OemFormProps> = ({
|
||||
onSubmit,
|
||||
onCancel,
|
||||
}) => {
|
||||
const { token, user } = useAuthStore();
|
||||
//const { token, user } = useAuthStore();
|
||||
const { toast } = useToast();
|
||||
|
||||
// Initialize the form with react-hook-form and zod resolver
|
||||
|
@ -23,20 +23,21 @@ import { OemMngForm } from "./components/OemMngForm";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { oemMng, PaginatedResponse } from "@/types/common/oemmng/oemmng";
|
||||
|
||||
const AccountsPage = () => {
|
||||
const OemMngPage = () => {
|
||||
const { token, user } = useAuthStore();
|
||||
const [isOpen, setIsOpen] = React.useState(false);
|
||||
const [editingUser, setEditingUser] = React.useState<User | null>(null);
|
||||
const [editingUser, setEditingUser] = React.useState<oemMng | null>(null);
|
||||
const [page, setPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
const { toast } = useToast();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
// Fetch users with pagination
|
||||
// Fetch OEMs with pagination
|
||||
const { data, isLoading } = useQuery<PaginatedResponse>({
|
||||
queryKey: ["users", page, pageSize],
|
||||
queryKey: ["oemMngs", page, pageSize],
|
||||
queryFn: async () => {
|
||||
const { data } = await api.get("/api/v1/admin/users", {
|
||||
const { data } = await api.get("/api/v1/app/oemmng/oemmngList", {
|
||||
|
||||
params: {
|
||||
page,
|
||||
limit: pageSize,
|
||||
@ -44,15 +45,15 @@ const AccountsPage = () => {
|
||||
});
|
||||
return {
|
||||
...data,
|
||||
users: data.users.map((user: User) => ({
|
||||
...user,
|
||||
Roles: user.Roles || [],
|
||||
oemMngs: data.oemMngs.map((oemMngs: oemMng) => ({
|
||||
...oemMngs,
|
||||
})),
|
||||
};
|
||||
},
|
||||
enabled: !!token,
|
||||
});
|
||||
|
||||
|
||||
const handlePageSizeChange = useCallback((newPageSize: number) => {
|
||||
setPageSize(newPageSize);
|
||||
setPage(1);
|
||||
@ -60,21 +61,21 @@ const AccountsPage = () => {
|
||||
|
||||
// Create user mutation
|
||||
const createMutation = useMutation({
|
||||
mutationFn: async (newUser: Partial<User>) => {
|
||||
mutationFn: async (newOem: Partial<oemMng>) => {
|
||||
// Include companyId in the user data
|
||||
const userWithCompanyId = {
|
||||
...newUser,
|
||||
const oemWithCompanyId = {
|
||||
...newOem,
|
||||
companyId: user?.companyId,
|
||||
};
|
||||
|
||||
const { data } = await api.post<User>(
|
||||
"/api/v1/admin/users",
|
||||
userWithCompanyId
|
||||
const { data } = await api.post<oemMng>(
|
||||
"/api/v1/app/oemmng/oemmngCreate",
|
||||
oemWithCompanyId
|
||||
);
|
||||
return data;
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["users"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["oemMngs"] });
|
||||
setIsOpen(false);
|
||||
toast({
|
||||
title: "고객사 생성",
|
||||
@ -92,15 +93,16 @@ const AccountsPage = () => {
|
||||
|
||||
// Update user mutation
|
||||
const updateMutation = useMutation({
|
||||
mutationFn: async (userData: Partial<User>) => {
|
||||
const { data } = await api.put<User>(
|
||||
`/api/v1/admin/users/${userData.id}`,
|
||||
userData
|
||||
mutationFn: async (oemData: Partial<oemMng>) => {
|
||||
const { data } = await api.put<oemMng>(
|
||||
`/api/v1/app/oemmng/oemmngUpdate/${oemData.id}`,
|
||||
|
||||
oemData
|
||||
);
|
||||
return data;
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["users"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["oemMngs"] });
|
||||
setIsOpen(false);
|
||||
setEditingUser(null);
|
||||
toast({
|
||||
@ -120,10 +122,10 @@ const AccountsPage = () => {
|
||||
// Delete user mutation
|
||||
const deleteMutation = useMutation({
|
||||
mutationFn: async (id: string) => {
|
||||
await api.delete(`/api/v1/admin/users/${id}`);
|
||||
await api.delete(`/api/v1/app/oemmng/oemmngDelete/${id}`);
|
||||
},
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ["users"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["oemMngs"] });
|
||||
toast({
|
||||
title: "고객사 삭제",
|
||||
description: "고객사가 삭제되었습니다.",
|
||||
@ -139,7 +141,7 @@ const AccountsPage = () => {
|
||||
});
|
||||
|
||||
// Table columns
|
||||
const columns: ColumnDef<User>[] = [
|
||||
const columns: ColumnDef<oemMng>[] = [
|
||||
{
|
||||
id: "index",
|
||||
header: "No",
|
||||
@ -151,13 +153,12 @@ const AccountsPage = () => {
|
||||
// header: "아이디",
|
||||
// },
|
||||
{
|
||||
accessorKey: "name",
|
||||
header: "업체명/고객사사",
|
||||
accessorKey: "oem_code",
|
||||
header: "업체명/고객사",
|
||||
},
|
||||
{
|
||||
accessorKey: "Department.name",
|
||||
header: "업체/고객사코드드",
|
||||
cell: ({ row }) => row.original.Department?.name || "-",
|
||||
accessorKey: "oem_name",
|
||||
header: "OEM 이름",
|
||||
},
|
||||
{
|
||||
accessorKey: "isActive",
|
||||
@ -220,17 +221,17 @@ const AccountsPage = () => {
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Users Table */}
|
||||
{/* OEMs Table */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>고객사 목록</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{data?.users && data.users.length > 0 ? (
|
||||
{data?.oemMngs && data.oemMngs.length > 0 ? (
|
||||
<>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={data.users}
|
||||
data={data.oemMngs}
|
||||
pagination={{
|
||||
pageIndex: page - 1,
|
||||
pageSize,
|
||||
@ -289,4 +290,4 @@ const AccountsPage = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export default AccountsPage;
|
||||
export default OemMngPage;
|
||||
|
@ -2,12 +2,14 @@
|
||||
|
||||
export interface oemMng {
|
||||
id: string;
|
||||
index: number;
|
||||
oem_code?: string | null;
|
||||
oem_name?: string | null;
|
||||
writer?: string | null;
|
||||
regdate?: Date | null;
|
||||
status?: string | null;
|
||||
oem_no?: string | null;
|
||||
isActive: boolean;
|
||||
}
|
||||
|
||||
export interface oemMngResponse {
|
||||
|
Loading…
Reference in New Issue
Block a user